Skip to content

Bind database policy claims as typed OData constants - #3756

Open
aaronburtle wants to merge 5 commits into
mainfrom
dev/aaronburtle/fix-odata-filter-injection
Open

Bind database policy claims as typed OData constants#3756
aaronburtle wants to merge 5 commits into
mainfrom
dev/aaronburtle/fix-odata-filter-injection

Conversation

@aaronburtle

@aaronburtle aaronburtle commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Why make this change?

Closes #3755

What is this change?

  • Replaces claim references in database policies with inert OData parameter aliases.
  • Keeps claim values separate from policy text in a ResolvedDatabasePolicy.
  • Injects claim values into the parsed OData AST as typed ConstantNode values before operand type promotion.
  • Converts supported primitive claim types to CLR values using invariant parsing and fails closed when a claim does not match its declared type.
  • Continues using database parameters for SQL predicates and now parameterizes Cosmos DB policy constants instead of inlining them.
  • Preserves legitimate claim values containing apostrophes, percent characters, or encoded text without decoding or rewriting them.

Relevant specification:

flowchart TD
    A["Trusted configured policy<br/>@item.ownerId eq @claims.userId"]
    B["Untrusted authenticated claim<br/>alice%27 or 1 eq 1 or %27"]

    A --> C["AuthorizationResolver"]
    B --> C

    C --> D["ResolvedDatabasePolicy"]
    D --> E["Policy:<br/>ownerId eq @dabClaim0"]
    D --> F["ClaimValues:<br/>@dabClaim0 maps to raw CLR string"]

    E --> G["ODataParser"]
    F --> H["ConstantNode map"]
    H --> G

    G --> I["ClaimsTypeDataUriResolver<br/>Resolves aliases before type promotion"]
    I --> J["ParameterAliasRewriter<br/>Resolves remaining aliases and Boolean contexts"]
    J --> K["Typed FilterClause AST"]

    K --> L["ODataASTVisitor"]
    K --> M["ODataASTCosmosVisitor"]

    L --> N["SQL predicate and provider parameters"]
    M --> O["Cosmos SQL predicate and provider parameters"]
Loading

How was this tested?

  • Integration Tests
  • Unit Tests

Focused unit-test coverage includes:

  • Literal apostrophes in string claims.
  • Percent-encoded text.
  • Double-encoded and mixed-encoded text.
  • Legitimate percent characters.
  • Typed boolean, integer, floating-point, and null claims.
  • Malformed primitive claims failing closed.
  • Cosmos DB policy constants being emitted as query parameters.

Sample Request(s)

No client-facing request contract changes are introduced.

Example database policy:

@item.ownerId eq @claims.userId

Example request:

GET /api/Note
Authorization: Bearer <token-with-userId-claim>
X-MS-API-ROLE: authenticated

A legitimate claim such as O'Brien or 50% complete is preserved exactly and bound as a database parameter. It is never inserted into or reinterpreted as OData policy syntax.

Copilot AI 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.

🟡 Changes recommended

There is a confirmed null-claim handling bug risk in type promotion (potential null TypeReference) and a shared-mutable-state concern in ResolvedDatabasePolicy.Empty that should be addressed before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR refactors database authorization policy processing to keep JWT claim values out of policy URI text by replacing claim references with inert OData parameter aliases, then injecting the claim values as typed OData AST constants (and parameterizing Cosmos DB constants) to prevent decoding/escaping issues and injection.

Changes:

  • Introduces ResolvedDatabasePolicy to carry policy text (with aliases) plus a separate alias→typed-value map, and updates ProcessDBPolicy to return it.
  • Extends OData parsing to accept pre-bound parameter alias nodes and resolves aliases to typed constants before operand type promotion.
  • Updates Cosmos policy predicate generation to emit constants as bound query parameters instead of inlining.
File summaries
File Description
src/Service.Tests/UnitTests/DwSqlQueryBuilderUpsertTests.cs Updates mock policy resolution to return ResolvedDatabasePolicy.Empty.
src/Service.Tests/UnitTests/DatabasePolicyClaimBindingUnitTests.cs Adds regression tests ensuring claim values stay out of policy text and become Cosmos parameters.
src/Service.Tests/Authorization/REST/RestAuthorizationHandlerUnitTests.cs Adjusts wildcard policy test to assert on .Policy.
src/Service.Tests/Authorization/AuthorizationResolverUnitTests.cs Updates policy parsing expectations to use aliases and validates typed claim values/fail-closed behavior.
src/Core/Resolvers/AuthorizationPolicyHelpers.cs Parses resolved policies with parameter alias nodes and injects claim constants into the OData parser.
src/Core/Parsers/ODataASTCosmosVisitor.cs Parameterizes Cosmos constant emission via a provided parameter factory delegate.
src/Core/Parsers/FilterParser.cs Adds optional parameterAliasNodes support and wires them into ODataUriParser.
src/Core/Parsers/ClaimsTypeDataUriResolver.cs Resolves aliases to claim constant nodes before type promotion.
src/Core/Authorization/AuthorizationResolver.cs Returns ResolvedDatabasePolicy, replaces claim substitution with alias binding, and adds invariant typed parsing of primitives.
src/Auth/ResolvedDatabasePolicy.cs Adds the new record type for policy+claim binding.
src/Auth/IAuthorizationResolver.cs Updates interface contract to return ResolvedDatabasePolicy.
Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 3
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread src/Core/Parsers/ClaimsTypeDataUriResolver.cs
Comment thread src/Auth/ResolvedDatabasePolicy.cs Outdated
Comment thread src/Core/Authorization/AuthorizationResolver.cs

@RubenCerna2079 RubenCerna2079 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.

Review still in progress, just want to leave the current comments before I continue.

Comment thread src/Auth/IAuthorizationResolver.cs Outdated
Comment thread src/Auth/ResolvedDatabasePolicy.cs
@aaronburtle

Copy link
Copy Markdown
Contributor Author

Copilot can you do another review please? Note I have intentionally removed ProcessDBPolicy, despite it being public because we are not concerned that external callers are taking dependency on this.

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.

please check on the feasibility for adding at least one E2E test for running in CI for all backends.

[DataRow("@item.rating eq @claims.emprating)", "rating eq 4.2)", DisplayName = "Valid policy parsing test for double claimvaluetype.")]
"(rating gt @dabClaim0) and (@dabClaim1 eq true)", DisplayName = "Valid policy parsing test for double and boolean claimvaluetypes.")]
[DataRow("@item.rating eq @claims.emprating)", "rating eq @dabClaim0)", DisplayName = "Valid policy parsing test for double claimvaluetype.")]
public void ParseValidDbPolicy(string policy, string expectedParsedPolicy)

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.

while most unit tests cover the changes, would it be possible to introduce an E2E test which runs in the CI for all the backends- MSSQL, MySQL, PostgreSQL etc.? like write for one and run for all..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Review In Progress

Development

Successfully merging this pull request may close these issues.

Improve claim binding in policies

4 participants