Summary
matchStringListConstraints (internal/auth/service_group.go) is an any-match (containsAny) matcher, used for the AccountIDs, Providers and Services constraint dimensions. It is safe today only because every caller happens to pass a single-element request list; nothing in the code enforces that.
The Regions dimension already had to be split out into matchAllRegionsConstraint for exactly this reason: an Azure RI exchange legitimately names several target locations at once, and under containsAny a caller permitted in one of them was granted all of them.
Risk
The first caller that submits a multi-value AccountIDs (or Providers / Services) request constraint silently reintroduces that same bypass class - a permission scoped to account A would authorise an operation spanning accounts A and B. It would look correct at the call site and no test would catch it.
Cheap hardening (behaviour-preserving today)
Either:
- assert
len(reqList) == 1 at the call sites that are documented to be single-valued, so a future multi-value request fails loudly instead of silently widening; or
- switch
AccountIDs (at least) to the all-match matcher, which is a no-op for single-element lists and correct for multi-element ones.
Low priority / latent: no known way to trigger it today, but the cost of closing it is near zero and the cost of it surfacing is an authorization bypass on a money path.
Found during adversarial review of PR LeanerCloud/cloud-commitments-cli#1515. Out of scope for that PR.
Summary
matchStringListConstraints(internal/auth/service_group.go) is an any-match (containsAny) matcher, used for theAccountIDs,ProvidersandServicesconstraint dimensions. It is safe today only because every caller happens to pass a single-element request list; nothing in the code enforces that.The
Regionsdimension already had to be split out intomatchAllRegionsConstraintfor exactly this reason: an Azure RI exchange legitimately names several target locations at once, and undercontainsAnya caller permitted in one of them was granted all of them.Risk
The first caller that submits a multi-value
AccountIDs(orProviders/Services) request constraint silently reintroduces that same bypass class - a permission scoped to account A would authorise an operation spanning accounts A and B. It would look correct at the call site and no test would catch it.Cheap hardening (behaviour-preserving today)
Either:
len(reqList) == 1at the call sites that are documented to be single-valued, so a future multi-value request fails loudly instead of silently widening; orAccountIDs(at least) to the all-match matcher, which is a no-op for single-element lists and correct for multi-element ones.Low priority / latent: no known way to trigger it today, but the cost of closing it is near zero and the cost of it surfacing is an authorization bypass on a money path.
Found during adversarial review of PR LeanerCloud/cloud-commitments-cli#1515. Out of scope for that PR.