Skip to content

fix: forward authFilter in RDS index query to prevent cross-tenant data exposure - #3504

Merged
Simone319 merged 3 commits into
mainfrom
fix/rds-index-auth-filter-bypass
Jul 1, 2026
Merged

fix: forward authFilter in RDS index query to prevent cross-tenant data exposure#3504
Simone319 merged 3 commits into
mainfrom
fix/rds-index-auth-filter-bypass

Conversation

@Simone319

Copy link
Copy Markdown
Contributor

Description of changes

The RDS secondary-index request mapping template (generateIndexQueryRequestTemplate in packages/amplify-graphql-index-transformer/src/resolvers/generators/rds-vtl-generator.ts) was not forwarding $ctx.stash.authFilter to the SQL Lambda function. This allowed any authenticated user to bypass owner/group row-level authorization by querying through auto-generated secondary-index query fields (e.g., listByRep), resulting in cross-tenant data exposure.

The fix adds constructAuthFilterStatement, constructNonScalarFieldsStatement, constructArrayFieldsStatement, and constructFieldMappingInput to the index query template — matching the standard list query path (packages/amplify-graphql-model-transformer/src/resolvers/rds/query.ts) which correctly applies these filters.

CDK / CloudFormation Parameters Changed

None — this change only affects the VTL request mapping template content generated for RDS-backed secondary index queries.

Issue #, if available

P463461136 (HackerOne report #3827340)

Description of how you validated changes

  1. Deployed a CDK app with a Customer model using @auth(rules: [{ allow: owner }]) and @index(name: "byRep", queryField: "listByRep") on a PostgreSQL data source
  2. Created two Cognito users (repA, repB) with separate private records
  3. With unfixed code (npm v1.21.3): repB calling listByRep(accountRepresentativeId: "REP_A") returns repA's private data (VULNERABILITY CONFIRMED)
  4. With fixed code: same query returns empty items: [] (VULNERABILITY BLOCKED)
  5. Verified owners can still query their own data through the index

Checklist

  • PR description included
  • yarn test passes
  • E2E test run linked
  • Tests are changed or added
  • Relevant documentation is changed or added (and PR referenced)
  • New AWS SDK calls or CloudFormation actions have been added to relevant test and service IAM policies
  • Any CDK or CloudFormation parameter changes are called out explicitly

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

…ta exposure

The RDS secondary-index request template (generateIndexQueryRequestTemplate)
was not forwarding ctx.stash.authFilter to the SQL Lambda, allowing any
authenticated user to bypass owner/group row-level authorization by querying
through auto-generated index query fields (e.g., listByRep).

Add constructAuthFilterStatement, constructNonScalarFieldsStatement,
constructArrayFieldsStatement, and constructFieldMappingInput to match the
standard list query path which correctly applies these filters.
@Simone319
Simone319 requested a review from a team as a code owner June 30, 2026 10:58
Simone Zhang and others added 2 commits June 30, 2026 13:23
…test

The RDS index query now calls constructNonScalarFieldsStatement and
constructArrayFieldsStatement which require ctx.output.getObject. Add the
mock to the test's constructMockContext helper.
Add a unit test that directly exercises RDSIndexVTLGenerator and asserts
the generated VTL contains ctx.stash.authFilter forwarding to the SQL
Lambda payload. This ensures the authorization bypass cannot regress.

@sarayev sarayev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it feasible to add an e2e for this against a real SQL data source? I'm thinking: deploy the repro schema (owner @auth model with a @index + custom queryField), seed rows for two users, then as user A call the index query with user B's key and assert none of B's rows come back. Would that fit into the existing RDS auth e2e suite, or is there a reason we can't test it end-to-end here?

@Simone319

Copy link
Copy Markdown
Contributor Author

Is it feasible to add an e2e for this against a real SQL data source? I'm thinking: deploy the repro schema (owner @auth model with a @index + custom queryField), seed rows for two users, then as user A call the index query with user B's key and assert none of B's rows come back. Would that fit into the existing RDS auth e2e suite, or is there a reason we can't test it end-to-end here?

The existing sql-pg-userpool-auth e2e suite already verifies that authFilter correctly blocks cross-owner access on the standard list query path (user2 cannot see user1's records via listTodoOwners). The list query and index query use the same SQL Lambda with the same filter application logic — the Lambda receives metadata.authFilter and appends it as a WHERE clause regardless of whether operation is "LIST" or "INDEX". The only difference was that the index resolver template wasn't forwarding the filter.

The new unit test directly asserts that generateIndexQueryRequestTemplate includes $ctx.stash.authFilter in the Lambda payload — the exact line that was missing. Since the authFilter value is computed by the same auth pipeline function and applied by the same Lambda code path, forwarding the identical filter produces the identical authorization guard.

TL;DR: The e2e already proves authFilter blocks cross-owner access on the list query. The unit test ensures the index query now forwards the same authFilter to the same SQL Lambda, which was missing before.

@Simone319
Simone319 merged commit 4601fde into main Jul 1, 2026
7 checks passed
@Simone319
Simone319 deleted the fix/rds-index-auth-filter-bypass branch July 1, 2026 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants