Skip to content

Enforce SMART compartment authorization on _include/_revinclude#5683

Open
feordin wants to merge 4 commits into
mainfrom
users/jaerwin/smart-include-candidate-authorization
Open

Enforce SMART compartment authorization on _include/_revinclude#5683
feordin wants to merge 4 commits into
mainfrom
users/jaerwin/smart-include-candidate-authorization

Conversation

@feordin

@feordin feordin commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the SMART-on-FHIR compartment leak where _include/_revinclude (and the $includes operation) returned referenced/referencing resources without compartment enforcement in the SQL provider.

Each include candidate row is now authorized in the include CTE itself: the candidate must be the compartment root, a shared/universal resource type, or provably reference the root via a formal compartment-definition search parameter ('EXISTS' probe on 'ReferenceSearchParam').

See 'docs/SmartIncludeCandidateAuthorization.md' and 'docs/arch/adr-2607-smart-include-compartment-scoping.md' for design details.

Related issues

Addresses [issue AB#196779].

Testing

  • New unit tests in SqlQueryGeneratorTests (predicate emission, membership factory rules, canary for the enforcement chain)
  • ~20 new SMART integration scenarios covering forward/reverse includes, :iterate, wildcards, $includes paging, Practitioner compartment, and SMART v2 granular scopes.

FHIR Team Checklist

  • Update the title of the PR to be succinct and less than 65 characters
  • Add a milestone to the PR for the sprint that it is merged (i.e. add S47)
  • Tag the PR with the type of update: Bug, Build, Dependencies, Enhancement, New-Feature or Documentation
  • Tag the PR with Open source, Azure API for FHIR (CosmosDB or common code) or Azure Healthcare APIs (SQL or common code) to specify where this change is intended to be released.
  • Tag the PR with Schema Version backward compatible or Schema Version backward incompatible or Schema Version unchanged if this adds or updates Sql script which is/is not backward compatible with the code.
  • When changing or adding behavior, if your code modifies the system design or changes design assumptions, please create and include an ADR.
  • CI is green before merge Build Status
  • Review squash-merge requirements

Semver Change (docs)

Patch|Skip|Feature|Breaking (reason)

feordin and others added 4 commits July 10, 2026 09:18
MSRC: SMART patient-scoped tokens could retrieve resources outside their
patient compartment via _include/_revinclude expansion (PHI disclosure).

Reuse the existing SmartCompartmentSearchExpression -> UNION-of-CTEs and
intersect it with the produced _include/_revinclude rows in the SQL query
generator, instead of the deprecated CompartmentAssignment table. Covers
SMART v1 and v2 granular scopes.

- SqlQueryGenerator: capture the compartment union, regenerate before the
  include table expansion, and EXISTS-intersect produced include/revinclude
  rows against the union.
- SqlCompartmentSearchRewriter (SMART-gated): additively enumerate every
  materialized type-specific reference param targeting the compartment root,
  because the compartment definition maps Encounter/Condition/Procedure/
  ImagingStudy to the non-materialized clinical-patient (resolve()-based)
  param. Every union member stays constrained to ReferenceResourceId =
  compartmentId, matching the SMART "any resource that refers to them" model.
- SearchOptionsFactory: map the reversed wildcard _revinclude=*:* produced
  "*" sentinel to DomainResource ("all compartment types") so every
  revincluded type is covered by the union.
- Add two integration repro tests (_include and _revinclude) plus the
  SmartCompartmentLeak.json fixture.
- ADR-2607 documents the issue, repro, and chosen union-intersection design.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Authorize include and revinclude candidates with immutable compartment membership rules while preserving includes paging and formal FHIR compartment semantics.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…quivalents

Tests: iterate and wildcard include/revinclude leaks, SMART v2 granular
scopes with includes, positive root/universal inclusion, multi-parameter
dedup, sort (_lastUpdated) with includes, and compartment-definition
guard tests for Patient and Practitioner. New leak-enforcement tests
skip on Cosmos DB (fix is SQL-only; Cosmos is deprecated) and the
compartment count assertion is now datastore-dependent.

Equivalence map: cover every resolve()-based (unmaterialized)
compartment parameter found in the R4 definitions - Patient adds
AuditEvent-patient (agent, entity), Basic/Invoice/MeasureReport-patient
(subject), Person-patient (link), Provenance-patient (target);
Practitioner adds Encounter-practitioner (participant) and
Person-practitioner (link). EpisodeOfCare-care-manager has no
materialized equivalent and is documented as a known gap.

Fixture: enable CoreFeatureConfiguration.SupportsIncludes in the SQL
integration fixture (production SQL configuration); without it the
includes continuation token is never created and the includes-paging
test could never pass.

The sort-by-search-parameter variant is blocked by a pre-existing
defect (#5672) and noted in the test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Remove orphaned sentinel comment in SearchOptionsFactory left from an
  earlier iteration.
- Remove unused skipJoinFromPreviousUnions parameter from
  AppendNewSetOfUnionAllTableExpressions and the no-op save/restore of
  _unionAggregateCTEIndex around the SMART v2 include regeneration.
- Add a fail-open detector in AttachSmartCompartmentMembership: if the
  request context is compartment-bound (fhirUser-derived
  CompartmentResourceType is set) and the expression contains include
  CTEs but no membership context could be constructed, log critical.
  System scopes never set CompartmentResourceType, so they cannot trip
  the detector.
- Add a canary unit test exercising the factory -> attach -> generate
  chain with the SearchOptionsFactory-shaped expression tree, so any
  regression that silently drops include authorization fails CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@feordin
feordin requested a review from a team as a code owner July 16, 2026 18:42
@feordin feordin changed the title Enforce SMART compartment authorization on _include/_revinclude results (SQL) Enforce SMART compartment authorization on _include/_revinclude Jul 16, 2026
@feordin feordin added Bug-Security Security related bugs. Area-SMART Azure Healthcare APIs Label denotes that the issue or PR is relevant to the FHIR service in the Azure Healthcare APIs Schema Version unchanged PaaS-breaking-change Add this label if your changes will require additional changes in PaaS repos to be complete labels Jul 16, 2026
@feordin feordin added this to the FY27\Q1\2wk\2wk01 milestone Jul 16, 2026
@feordin feordin added the ADR-Included ADR Included in the PR label Jul 16, 2026
Comment on lines +185 to +196
foreach (string equivalentCode in equivalentCodes)
{
if (SearchParameterDefinitionManager.Value.TryGetSearchParameter(resourceType, equivalentCode, out SearchParameterInfo equivalent)
&& !string.Equals(equivalent.Url.AbsoluteUri, parameter.Url.AbsoluteUri, StringComparison.Ordinal)
&& equivalent.Type == SearchParamType.Reference
&& equivalent.IsSupported
&& equivalent.TargetResourceTypes?.Contains(compartmentType, StringComparer.Ordinal) == true)
{
parameters[equivalent.Url.AbsoluteUri] = equivalent;
equivalentFound = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ADR-Included ADR Included in the PR Area-SMART Azure Healthcare APIs Label denotes that the issue or PR is relevant to the FHIR service in the Azure Healthcare APIs Bug-Security Security related bugs. PaaS-breaking-change Add this label if your changes will require additional changes in PaaS repos to be complete Schema Version unchanged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants