Skip to content

fix(redis-schema): copy policy schemas instead of mutating shared tables#13555

Merged
shreemaan-abhishek merged 2 commits into
apache:masterfrom
janiussyafiq:fix/redis-schema-shared-tables
Jun 22, 2026
Merged

fix(redis-schema): copy policy schemas instead of mutating shared tables#13555
shreemaan-abhishek merged 2 commits into
apache:masterfrom
janiussyafiq:fix/redis-schema-shared-tables

Conversation

@janiussyafiq

@janiussyafiq janiussyafiq commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

apisix/utils/redis-schema.lua exports policy_to_additional_properties (the shared redis / redis-cluster schema tables) for reuse by limit-count, limit-req, and limit-conn. limit-conn's key_ttl variants were derived by mutating those shared tables in place at module load:

local limit_conn_redis_schema = policy_to_additional_properties["redis"]
limit_conn_redis_schema.properties.key_ttl = {
    type = "integer", default = 3600,
}

Because the table is shared, key_ttl (and its default of 3600) leaked into the base tables seen by every other consumer. Observable effect: limit-count and limit-req confs silently gained a key_ttl = 3600 field during schema validation, even though neither plugin understands the property. A consumer-side core.table.deepcopy(redis_schema.schema) doesn't help — by load time it copies the already-mutated tables.

Changes

  • apisix/utils/redis-schema.lua — derive limit-conn's key_ttl variants from core.table.deepcopy of the shared base, then add key_ttl to the copy. The shared redis / redis-cluster tables stay clean. deepcopy is the schema-derive idiom already used by limit-count, and it yields a fully independent tree so future nested edits cannot leak back into the shared base.
  • apisix/plugins/limit-req.lualimit-req bound redis_schema.schema directly (a live reference to the shared module table). It only reads it today, but the alias is a latent footgun: any future in-place mutation reachable through limit-req would poison every other consumer. deepcopy the schema at load time so limit-req owns an independent copy, matching limit-count and ai-cache.

Behavior

  • limit-conn is unchanged: key_ttl still accepted, default 3600 still applied.
  • limit-count / limit-req confs no longer get a foreign key_ttl injected. A stray explicit key_ttl on those plugins is still tolerated (their schemas do not reject additional properties); it is simply no longer injected.
  • Backward compatible.

Tests

t/utils/redis-schema.t:

  • one block loads limit-conn first (so any shared-table leak would be visible) and asserts limit-count / limit-req confs across redis and redis-cluster do not gain key_ttl;
  • a second block pins limit-conn's own behavior (default 3600 applied, explicit value honored).

Which issue(s) this PR fixes:

None (pre-existing latent bug found while building on redis-schema).

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change (no doc change needed: internal schema utility; user-facing behavior unchanged)
  • I have verified that this change is backward compatible

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Jun 12, 2026
@janiussyafiq janiussyafiq force-pushed the fix/redis-schema-shared-tables branch from e0b73a9 to 64795a2 Compare June 12, 2026 17:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a schema cross-contamination bug in apisix/utils/redis-schema.lua where limit-conn extended Redis policy schemas by mutating shared tables at module load time, causing unrelated plugins (e.g., limit-count, limit-req) to unexpectedly inherit key_ttl (and its default) during schema validation.

Changes:

  • Build limit-conn’s Redis / Redis Cluster schema variants via a copied properties table (instead of mutating the shared base tables).
  • Add regression coverage to ensure key_ttl is not injected into limit-count / limit-req configs even when limit-conn is loaded first.
  • Add assertions that limit-conn still applies key_ttl defaulting and honors explicit key_ttl.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
apisix/utils/redis-schema.lua Avoids mutating shared Redis policy schema tables by creating limit-conn-specific variants with copied properties.
t/utils/redis-schema.t Adds regression tests to prevent key_ttl leakage into other plugins and to pin limit-conn’s key_ttl behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…bles

limit-conn's key_ttl schema variants were built by mutating the shared policy_to_additional_properties tables in place, leaking key_ttl (and its default of 3600) into every other consumer of apisix/utils/redis-schema.lua: limit-count and limit-req confs silently gained a key_ttl = 3600 field during validation.

Build the limit-conn variants from core.table.deepcopy of the shared base so the extra property stays local to limit-conn. deepcopy matches the schema-derive idiom already used by limit-count and ai-cache, and yields a fully independent tree so future nested edits cannot leak back into the shared base.

Add t/utils/redis-schema.t: one block loads limit-conn first and asserts limit-count/limit-req confs across redis and redis-cluster do not gain a key_ttl; a second block pins limit-conn's own behavior (default 3600 applied, explicit value honored).

Signed-off-by: janiussyafiq <izzraff.js@gmail.com>
limit-req bound redis_schema.schema directly, holding a live reference to the shared module table. It only reads the table today, so nothing leaks, but the alias is a latent footgun: any future in-place mutation reachable through limit-req would poison every other consumer.

deepcopy the schema at load time to match limit-count and ai-cache, so limit-req owns an independent copy. No behavior change.

Signed-off-by: janiussyafiq <izzraff.js@gmail.com>
@janiussyafiq janiussyafiq force-pushed the fix/redis-schema-shared-tables branch from adc3a8c to c7dfd28 Compare June 22, 2026 01:45
@shreemaan-abhishek shreemaan-abhishek merged commit 3494117 into apache:master Jun 22, 2026
23 checks passed
@janiussyafiq janiussyafiq deleted the fix/redis-schema-shared-tables branch June 22, 2026 06:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants