Skip to content

Add reproduction test for v1 admin authz param-duplication bypass#13

Open
markrmiller wants to merge 6 commits into
mainfrom
claude/solr-param-duplication-authz-3O2z7
Open

Add reproduction test for v1 admin authz param-duplication bypass#13
markrmiller wants to merge 6 commits into
mainfrom
claude/solr-param-duplication-authz-3O2z7

Conversation

@markrmiller

Copy link
Copy Markdown
Owner

Demonstrates that custom (params-based) RuleBasedAuthorization rules match
with any-value semantics (Permission.load + context.getParams().getParams())
while the admin handlers execute on the first value (params.get -> arr[0]).
Supplying a security-relevant param twice lets a decoy authorized value make a
narrow permission govern the request while the handler runs a different,
unauthorized value (e.g. RELOAD allowed_collection authorizes DELETE
forbidden_collection).

The test asserts current (vulnerable) behavior and so serves as a
reproduction; once a fix lands, the bypass cases should assert FORBIDDEN.

https://claude.ai/code/session_019x1K815brY4h1FHr2SgyKr

claude added 6 commits May 25, 2026 11:34
Demonstrates that custom (params-based) RuleBasedAuthorization rules match
with any-value semantics (Permission.load + context.getParams().getParams())
while the admin handlers execute on the first value (params.get -> arr[0]).
Supplying a security-relevant param twice lets a decoy authorized value make a
narrow permission govern the request while the handler runs a different,
unauthorized value (e.g. RELOAD allowed_collection authorizes DELETE
forbidden_collection).

The test asserts current (vulnerable) behavior and so serves as a
reproduction; once a fix lands, the bypass cases should assert FORBIDDEN.

https://claude.ai/code/session_019x1K815brY4h1FHr2SgyKr
…ion read authz

A search request's entry-node authorization is keyed on the URL/path
collection, while distributed execution routes by the 'collection' request
param (CloudReplicaSource), and a form-body 'collection' value is invisible
to the entry-node check. This test demonstrates that the asymmetry does not
leak data across collections: the fan-out shard sub-request is independently
re-authorized against the original principal and denied (403). Guards that
per-shard re-authorization control against regression.

https://claude.ai/code/session_019x1K815brY4h1FHr2SgyKr
…ollection admin

A user restricted by a custom params permission to a single harmless action
(LIST) escalates to deleting arbitrary collections by supplying 'action' more
than once: authorization matches the decoy LIST value (any-value semantics)
while CollectionsHandler executes the first value (DELETE). Unlike distributed
search, collection admin has no second authorization gate before Overseer
execution, so the unauthorized DELETE actually runs. Covers both the URL-vs-form
-body split and pure URL duplication.

Also documents that duplicating 'name' (per the original hypothesis) instead
breaks the DELETE because 'name' is consumed as a multi-value array, so the
working exploit duplicates only 'action'.

https://claude.ai/code/session_019x1K815brY4h1FHr2SgyKr
Probes whether naming an authorized collection alongside the target (comma-list
in body/URL, triple duplication, target-first) can get the shard sub-request
past the target node's authorization, exploiting that authorize() is any-of over
the collection list. It cannot: a shard sub-request targets a specific core and
is re-authorized against that core's own collection, and the forwarded
'collection' value travels in the sub-request body -- invisible to the URL-only
params that build the authorized collection list at the target -- so the target
never short-circuits on the authorized collection.

https://claude.ai/code/session_019x1K815brY4h1FHr2SgyKr
V2HttpCall parses a JSON request body into a content stream, not into SolrParams;
the command is parsed only at execution time via getCommands(), after authorize().
So a custom RuleBasedAuthorization permission keyed on params:{name:...} cannot see
a name supplied in a V2 JSON command body. The test pins this with an A/B on
POST /____v2/collections: a guard rule keyed on params:{name:supersecret} denies
the request when name is in the query string, but is bypassed (collection created)
when the same name is in the JSON body.

https://claude.ai/code/session_019x1K815brY4h1FHr2SgyKr
…t-value params

Per-shard re-authorization protects the 'collection' dimension because the
receiving core re-derives the authorized collection from the core it serves. A
custom rule keyed on any other first-value param (here q) gets no such
protection: the shard sub-request carries the full multi-valued param, so the
receiving core makes the same any-value decision and then executes the first
value. Demonstrates a 'pubonly' role allowed only q=id:pubdoc retrieving the
secret doc via q=id:secretdoc&q=id:pubdoc.

https://claude.ai/code/session_019x1K815brY4h1FHr2SgyKr
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