Scope parameters that are accepted but never enforced: kcp identity-header smuggling, Convoy list-vs-get IDOR, Perses query-param tenancy, Moquette Will-path ACL gap, and Obot OAuth audience confusion¶
Source: hourly offensive-security scan of GitHub Security Advisories, 2026-09-18 (waves published 2026-09-18T17:15Z–17:59Z).
One theme ties the durable records: the authorization layer and the data layer disagree about what scopes the request. A URL path parameter that gates the permission check is dropped before the query runs; a query parameter silently overrides the path scope; a protocol side-channel (MQTT Will, proxy identity header) reaches a sink the main path guards; an HTTP method collapses onto the wrong action; a signed digest field is never re-verified. Each is a separate, immediately testable audit axis.
Advisory table¶
| Product | GHSA | CVE | CVSS | Boundary |
|---|---|---|---|---|
| kcp front-proxy | GHSA-c8w2-fgvx-vhv4 | CVE-2026-61682 | 9.9 | Front-proxy appends X-Remote-User/X-Remote-Group/X-Remote-Extra-* instead of replacing them, and never strips client-supplied copies. The shard trusts these headers as verified identity over the mTLS front-proxy leg → any ordinary credential asserts X-Remote-Group: system:masters and owns every workspace on the shard. Complete multi-tenant isolation break |
| Convoy | GHSA-p5vg-v7mj-f6q4 | CVE-2026-81505 | high (no patch ≤ v26.6.2) | GET /api/v1/projects/{projectID}/sources/{sourceID}: authorization resolves {projectID}, then FindSourceByID(ctx, projectID, id) accepts projectID and never uses it — the SQL has no project_id predicate at all. Any caller authorized on any one project reads any project's Source by ID, including plaintext AMQP/Kafka/SQS broker credentials in pub_sub.*.auth.password. The list endpoint scopes correctly; only single-item get drifts |
| Perses (filesystem DB) | GHSA-vr5f-w35q-98jp | CVE-2026-63445 | high | List endpoints bind the project query parameter into the resource Query and use it to build filesystem paths; path validation (Flatten) runs only for Create/Update bodies, never list queries → ?project=../projects reads arbitrary YAML/JSON from the host |
| Perses (project scoping) | GHSA-cjgj-2fwf-4c2w | CVE-2026-63458 | high | GET /api/v1/projects/team-a/dashboards?project=finance-secret — caller is authorized on the path project, then the query param re-points the data lookup at another tenant → viewer on one project lists every project's dashboards/datasources/variables. Defeats all project-level isolation on project-scoped reads |
| Perses (datasource proxy) | GHSA-4227-9989-jrhx | CVE-2026-63199 | high | Proxy authorizes the caller on the Datasource scope, then resolves and decrypts any Secret named in the request body with no Secret-scope check. Hold only GlobalDatasource:create → attach a Secret you cannot read to a datasource you control → the proxy returns it in plaintext. Classic confused deputy: the server's credential access is steered by the caller's body |
| Moquette MQTT broker | GHSA-9jjc-fw8x-fmwx | CVE-2026-85058 | 7.5 | All three QoS PUBLISH handlers call authorizator.canWrite(), but the Will-message path (fireWill() → publishWill() → publish2Subscribers()) skips it entirely. Set an ACL-restricted topic as your Will Topic in CONNECT (anonymous is the default), drop the TCP connection with RST, and the broker publishes your payload to the protected topic on its own behalf |
| Obot (MCP platform) | GHSA-xwmw-prc4-v3cr | — | 8.8 | OAuth dynamic client registration unauthenticated + unrestricted redirect URIs, authorization auto-completes with no consent screen for a logged-in victim, and the minted token carries the victim's full group set — the field that authorizes API paths — while the server validates issuer but not audience. One victim click → bearer token valid across the victim's whole API, not just the requested MCP server |
| Obot (MCP platform) | GHSA-jgh3-fggc-mcpm | — | 7.6 | Remote MCP server URL validated only as parseable http/https; the controller auto-fetches OAuth discovery metadata during reconcile (no user interaction beyond registration) with no loopback/link-local/RFC1918/169.254.169.254 guard on the default config, and reflects the response body in error messages → non-blind SSRF straight to cloud IAM creds. The one egress flag is off-by-default, loopback-only, and absent from the metadata path |
| Obot (MCP platform) | GHSA-pr6h-vr44-xq8j | — | medium | Registry auth authorizer rejects a fixed set of known-protected prefixes and default-allows everything else; the registry mounted at a top-level prefix outside the set stays anonymous-readable. Fix for a prior CVE protected one prefix, not the new one — denylist-of-protected-routes drift |
| AnyCable Pusher REST API | GHSA-5p54-whvp-x327 | CVE-2026-63405 | medium | HMAC stringToSign includes the body_md5 query param verbatim but the server never computes md5(actual body) to compare, and there is no auth_timestamp staleness check. Capture one signed request (LB logs) → replay the same query string with an entirely different body, forever |
| zot OCI registry | GHSA-qg67-7m6v-qg25 | CVE-2026-61833 | 8.1 | Bearer handler maps every non-GET/HEAD method to the "push" action (action = "pull"; if method != GET && != HEAD { "push" }) — DELETE is never "delete" — and the fine-grained DistSpecAuthzHandler middleware is bypassed entirely for bearer-authenticated requests. Push-scope-only token deletes arbitrary manifests/blobs, violating the Docker Distribution token spec |
Folds on this run:
- process-compose MCP SSE DNS rebinding (CVE-2026-77339/GHSA-5gm3-9crp-6g3v) → folded onto the Sept 15 MCP DNS-rebinding page: SSE is the default transport when
transportis omitted, theX-PC-Token-Keymiddleware is installed on the Gin REST router only — the separately-started MCP listener does not inherit it — and the registered control tools start/stop/restart/scale processes and truncate logs. New axis for the rebinding matrix: when a tool has two listeners (API + MCP), test auth per-listener, not per-product. - md-editor-v3 XSSPlugin bypass (CVE-2026-84992/GHSA-3rm2-h79c-8qw6) → folded onto the Sept 16 sanitizer-gaps page: the
highlightcallback interpolates the fenced-code language/info string intoclass="language-${language}"and an unquotedlanguage=${language}attribute unescaped; the shippedXSSPluginfilters onlyhtml_block/html_inlineparser tokens, so renderer-generated HTML is invisible to it. Payload```x"><details/open/ontoggle=alert(1)>executes with the "XSS plugin" enabled. Rule: a sanitizer that inspects parsed tokens does not cover markup the renderer constructs — fuzz the info-string/attribute-construction path separately from the raw-HTML path. - ToolHive
host.docker.internallateral movement (CVE-2026-58197/GHSA-qg2g-g9w3-m5h8, 8.8) — defaultnetworkpermission profile isinsecure_allow_all: true, so a containerized MCP server reaches the ToolHive control-plane MCP endpoint and every other managed proxy on host localhost, which are themselves unauthenticated. Added to the MCP cluster note below: test the container-to-host-local hop on any MCP runner (ToolHive, similar supervisors) before theorizing an escape.
Tracked without publication: Mnemosyne sync server decodes JWT header/payload without verifying the HMAC signature — any well-formed token, including alg:none and empty signature, authenticates as any user_id (CVE-2026-59163/GHSA-xcw4-53cc-hv32, 9.1; decode-instead-of-verify is already covered by the existing JWT-decision-table guidance — sweep signature verification explicitly whenever a custom auth wrapper parses JWTs by hand); Paymenter downgrade double-spend race — unlocked Service::upgradable eligibility read + no DB::transaction/lockForUpdate lets N concurrent downgrades each refund (CVE-2026-71537/GHSA-5gmm-hjfj-8ff7; the payment-flow TOCTOU axis is worth a scripted concurrent-replay pass on any billing target you hold scope on); File Viewer legacy .doc renderer emits hyperlink targets with HTML escaping but no URL-scheme allowlist → javascript:/vbscript:/data: links live in the embedding origin (CVE-2026-91127/GHSA-3753-m2x2-q623 — same escaped-but-not-scheme-allowlisted link class as the Sept 16 MDC scheme-miss item, folded into the Sept 16 sanitizer page this run); AnyCable telemetry hardcoded authToken="secret" + raw os.Args (incl. --secret=) shipped to a third-party endpoint (CVE-2026-63406/GHSA-w72w-9qmj-c9qm — operator hygiene, no target workflow); AnyIO extra_groups variable mix-up + IDNA 2003 TLS-hostname divergence; adm-zip declared-size DoS; Bouncy Castle lazy-ASN.1 nesting-guard reset; kcp/zot siblings. Note: the kcp advisory's CVE identifier CVE-2026-61682 collides with the CVE assigned to the Sept 18 Semantic MediaWiki stored XSS — a GitHub-advisory-side duplicate assignment; when citing this CVE, disambiguate by product.
Why this is worth an operator page¶
- Identity headers over an "internal" mTLS leg are only as trusted as the proxy's strip behavior. kcp's shard trusts
X-Remote-*because it only accepts the front-proxy connection — but the front-proxy appended instead of replaced. Sweep every reverse-proxy/aggregation hop on a target: send your ownX-Remote-User,X-Remote-Group,X-Forwarded-User,X-Auth-Subjectstyle headers and diff the downstream verdict. This is the header twin of Sept 18's Nextcloud request-URI-identity drift and the classicX-Forwarded-Foridentity steering. - Compare list vs single-item authorization, always. Convoy's list endpoint scoped by project; the get-by-ID endpoint's service function took the project ID and dropped it before SQL. For every collection you can list, fetch one foreign-tenant item by ID through the same route pattern and diff. A parameter present in the handler signature but absent from the query is grep-able in any open-source target (
FindSourceByID(ctx, project.UID, sourceID)→WHERE s.id = $1). - Query parameters that shadow path scope are tenant-isolation holes. Perses authorized the
{projectID}in the URL and then let?project=re-point the lookup. For every path-scoped collection route, append the common scope words as query params (project,namespace,tenant,org,group,scope) and diff result sets against the path value. - Validation asymmetry between write and read paths leaks paths and rows. Perses validated the project string on Create/Update bodies but not list queries — traversal landed in the read path. When you find a validated input on POST, immediately test the same field on GET/list/search endpoints.
- Confused-deputy decrypt: authorize the wrapper, then name the secret. If any endpoint that authorizes scope A will resolve/decrypt/attach an object from scope B named in your body, you own a secret oracle. Enumerate endpoints where a body field names a credential/secret/config object and check the response path returns the plaintext (datasource "connectivity check" features are favorite hosts for this).
- Protocol side-channels re-implement publish. The broker checks ACLs on client PUBLISH but not on the Will it publishes itself after an abrupt disconnect. For any broker (MQTT/Kafka hooks/webhooks/AMQP), list every way a message reaches subscribers (direct publish, Will/LWT, retained messages, delayed/queued redelivery, system events) and diff which paths hit the authorization call.
- Signed-digest fields that the server never recomputes are signature bypasses with extra steps. AnyCable's
body_md5was in the HMAC string but never compared against the body. When an API signature scheme includes a body digest, hash a different body yourself and replay; also testauth_timestampstaleness independently. - Method-to-action mapping collapse. zot turned DELETE into "push" and skipped the fine-grained authorizer for bearer tokens. When a token has narrow scopes, replay it against every HTTP method on the same route — especially destructive verbs — and check whether a different authorization middleware applies per auth mechanism (bearer vs basic).
- OAuth audience confusion via dynamic client registration. Unauthenticated DCR + unrestricted redirect + no consent + a token carrying the resource-server's authorization groups + issuer-checked-but-audience-ignored = one-click token theft. On any OAuth-capable platform with DCR enabled: register a client with your own redirect, craft the authorize URL, check whether a consent screen actually renders, and (with your own second lab account) inspect which claims/groups the minted token carries vs its
aud, then test it against the general API, not just the MCP endpoint. - Reflected-error SSRF beats blind SSRF. Obot's metadata-sync SSRF returns the fetched body inside error messages and fires automatically on reconcile. When registering attacker-controlled URLs into any platform (MCP servers, webhooks, OAuth issuers, remote configs), test whether platform-generated error text echoes upstream response bytes.
- Default-allow authorizers are denylists of protected routes. Obot's second advisory: the authorizer enumerated known-protected prefixes and allowed everything else; every new top-level mount is anonymous until someone remembers to add it. Enumerate top-level route prefixes on authed products and re-check 401-vs-200 on each — especially mounts added recently.
Validation workflow (authorized scope only)¶
Disposable tenants and synthetic objects only
Build two disposable projects/workspaces/tenants with a canary credential object in the victim tenant and prove cross-tenant read with a synthetic broker password or canary secret. For header smuggling, forge identity headers only against lab front-proxies you own and record authorization verdicts — never assert system:masters against shared infrastructure. For OAuth proofs use two of your own lab accounts and an owned redirect domain; revoke tokens immediately. For MQTT, run a lab broker with fake ACL topics; never Will-publish into production topics.
- List-vs-get IDOR sweep. For every collection route: enumerate as user A in tenant A, then fetch one foreign item ID via the single-item route from tenant B; diff status + body. Grep open-source targets for service functions that take a scope/tenant parameter and never reference it in the query.
- Query-shadow probe. For every path-scoped collection, append
?project=,?namespace=,?tenant=,?org=pointing at a foreign scope; compare payloads against the path-scoped positive control. - Read-path validation probe. Take any input the API rejects as malformed/invalid on POST and send it raw on GET/list/search; watch for traversal artifacts (
../list results, path echoes, 500s with path fragments). - Broker side-channel matrix (MQTT labs). Per ACL policy: direct PUBLISH verdict, Will-message verdict (CONNECT with restricted Will topic + TCP RST), retained-message verdict, subscription verdict. Moquette proved the Will leg is the one implementers forget.
- Method/scope replay. With a narrowly scoped bearer token, replay DELETE/PUT/PATCH against routes where the token scope should deny, and compare bearer vs basic-auth verdicts on the same route.
- Digest-replay test. Capture any signed request with a body-digest parameter; resend the same query string with a mutated body; also resend with an old
auth_timestamp. - Front-proxy header strip check. Send forged
X-Remote-*/identity headers through each proxy hop chain in scope and diff downstream identity-derived decisions (lab targets only). - DCR/audience audit (owned accounts). Register a DCR client, walk the authorize flow with a second lab account, record consent-screen presence, decode the minted token's groups/aud, and test the token against the general API surface.
Reporting checklist¶
- State the exact scope parameter that was ignored (path vs query vs service-layer argument) and show the positive control (same request, in-scope object) beside the cross-tenant result.
- For credential leakage, quote only the canary value and name the JSON field; never paste live broker passwords.
- For header smuggling, include the proxy hop diagram and the raw request/response verdict table.
- For races (Paymenter-style), report concurrency count, observed N× credit delta, and whether idempotency keys exist.
- Version/commit pin each verdict (Convoy has no patch as of v26.6.2 — mark as disclosure-pending).
September 23 follow-up: share-link minting skips the ownership check the read path enforces (Photoview)¶
- Photoview through 2.4.0
shareAlbumGraphQL mutation authorization bypass (CVE-2026-96271, GHSA-r72q-r5vm-p79c, high): any authenticated user supplies an arbitrary album ID to the share-link mutation and receives a working share token for a victim-owned album — photos and sub-albums exposed to anyone with the link, and the attacker retains indefinite control over the token's settings (expiry/password) because they minted it. The read path checks ownership; the link-creation path checks only "is authenticated." - Durable axis, sibling of the list-vs-get drift above: token/share/invite minting endpoints are authorization sinks independent of the read endpoint they gate. The share feature is commonly a separate resolver written later, with only a login gate. Sweep rule: for every object type with a share/public-link capability, enumerate the mutation that creates the link and replay it with foreign object IDs from your second lab account — a 200 with a usable token is the finding even if every read route tests clean. Two extra report dimensions unique to this class: the minted link survives the victim revoking their own shares (attacker-controlled token settings), and the exposure audience is "anyone with the link," so severity includes unauthenticated third parties, not just the attacker.
- Validation stays two-account: disposable user B mints a share for user A's album containing one canary photo, fetch the public link anonymously, record album ID field name, token TTL, and which settings the minter can still change. Never mint links against real instances' shared content beyond an owned canary album.
September 23 follow-up: iFlytek astron-agent — debug-tool URL fetch is SSRF, mapper-XML sort direction is SQLi (2 GHSAs)¶
UrlCheckTool.checkUrlendPointSSRF on thedebugToolV2API (CVE-2026-95930, GHSA-v9wr-gmhf-jxmg, ≤1.0.6): an AI-agent platform ships a "URL check" debug tool whose endpoint fetches caller-supplied URLs server-side — the tool the developers built for agents is itself an unauthenticated-position outbound-fetch primitive. Agent-platform recon heuristic: enumerate tool/debug route families (debugTool*,tool/test*,preview*) separately from chat/model APIs; every agent tool that accepts a URL or webhook is an SSRF candidate the platform's own input validators don't cover (join the CuteNews upload-from-inet and credential-test-exfil exemplars).getBotListsortDirectionSQLi via MyBatis mapper XML (CVE-2026-95929, GHSA-v3xv-f287-j6jx, ≤1.0.7): the sort direction rides intoChatBotMarketMapper.xmlunsanitized — the same sort-field-into-SQL class as the TDuckCloudconcatOrderByfold on the Sept 18 query-grammar page, here via raw${}interpolation in the mapper file instead of the pagination interceptor. Sweep both spellings: framework-levelconcatOrderByconfig and per-query mapper XML${sortDirection}/${orderBy}substitutions; on closed-source targets, probesort/order/dir/ascparams on every list endpoint including the marketplace/bot-catalog public ones.
September 23 evening follow-up: Moquette pattern-ACL wildcard injection — your identity string is a glob (CVE-2026-85724 / GHSA-5f42-97gr-vfhq, CVSS 9.6)¶
- Moquette pattern-based ACL cross-tenant bypass (GHSA-5f42-97gr-vfhq, critical): multi-tenant MQTT deployments use pattern ACL rules like
sensor/%c/#(per-client topic trees).AuthorizationsCollector.canDoOperationsubstitutes clientId/username raw into the pattern and then wildcard-matches it, and the clientId is never validated for MQTT wildcard characters (+,#) at CONNECT. A client that connects with clientId+turnssensor/%c/#intosensor/+/#— cross-tenant read and write on every sibling tenant's topic tree from one CONNECT field. Same advisory also ships a broker-wide DoS (theSessionEventLoopcatches onlyInterruptedExceptionand is never restarted, so any uncaught exception — malformed$share/SUBSCRIBE, deep topic nesting, ACL NPE — wedges all co-located clients) and a durable-session cross-session corruption bug. - Durable axis, generalized beyond MQTT: when an authorization policy interpolates an identity string into a pattern/glob/template, the identity becomes pattern syntax — inject the metacharacters into the identity itself. This is the same class as sudoers glob metacharacters in usernames,
sudo -U#runas-list wildcards, AWS:${aws:username}policy-variable interpolation, nginxmap/rewrite matches on URI fragments, and every "per-user home/folder/topic path" ACL. Sweep rule: wherever a policy engine documents%c/%u/{user}/${username}-style substitution into a wildcard-matched path, connect/register/provision with an identity composed of that engine's metacharacters (+,#,*,?,{},..) and diff the resulting authorization scope. The precondition (pattern ACL configured) is itself recon-visible — multi-tenant topic-naming conventions in docs/clients tell you the pattern shape before you ever connect. - Validation boundary: lab broker only, your own two disposable clientIds (
normalvs+), synthetic sensor payload markers on tenant A and B topics; never inject wildcards into a production broker's live client namespace (the bypass also grants write, so a stray publish is data corruption for other tenants). - Moquette is now a two-advisory product on this page (Will-path ACL skip CVE-2026-85058 above, pattern-wildcard injection here): broker ACL audits should enumerate every leg that moves a message or matches a policy — direct PUBLISH, Will, retained, subscribe, pattern substitution — and check the guard on each; the implementers consistently forget the asynchronous legs.
Tracked from the Sept 23 wave without publication¶
- ClipBucket v5 before 5.5.3 unauthenticated time-based blind SQLi in the photo-search
queryparameter into WHERE + ORDER BY (CVE-2026-96272) — search-field class already covered by the sort-field sweep rules; tracked. - Fast FAC1203R device-discovery
copy_msg_elementstack overflow with public exploit (CVE-2026-96257) — router/AP discovery-service memory-safety class per Totolink/Netcore precedent. - Ghidra before 12.1.4
OptionsDB.createUnregisteredOptionTYPE_COL unchecked → malicious.gdbimport permanently locks domain objects (CVE-2026-96273) — analyst-tool DoS, no operator escalation axis. - Manalyze PE
BlockSizeinteger underflow (CVE-2026-95958) — parser memory-safety; fetchmail NTLM Type-2 challenge stack overflow (CVE-2026-94184) re-surfacing, already tracked on the Sept 21 page.
Related pages: the Sept 18 Nextcloud app-layer authority drift (request-derived identity + sibling-route authz drift), the Sept 15 MCP DNS-rebinding cluster (process-compose/ToolHive fold target), the Aug 6 agent tool-policy boundary page (MCP platform trust boundaries), the Sept 16 rmcp OAuth metadata page (OAuth binding misses; md-editor-v3 fold target), and the Aug 5 outbound-fetch page (agent debug-tool SSRF fold sibling).
October 6 follow-up: External Secrets Operator webhook generator clears its own label-enforcement flag at init (GHSA-q7hv-xx6h-q2x8 / CVE-2026-26287, 7.1)¶
A pure initialization-order bug with a tenant-isolation payload: the webhook generator sets EnforceLabels and then a later init step overwrites it back to false, so the provider-side check "referenced secret must carry external-secrets.io/type=webhook" is silently skipped. A user with permission to create Webhook generators can point one at any Secret in reach (never labelled for webhook use) and have the controller fetch-and-forward it to a URL in the generator spec — secret exfiltration across the label boundary the platform believes is enforcing tenancy.
Operator value on Kubernetes engagements:
- This is the page's central invariant ("the enforcement layer and the data layer disagree about scope") in its most mechanical form: the scope predicate existed, was correctly written, and was destroyed by assignment order in the constructor. Code-audit fingerprint when you have source: grep for
Enforce/Required-style boolean flags and diff set-ordering insideNew()/SetupWithManager()/reconcile-init paths — aflag = truefollowed by a struct copy or defaulting pass (if s.Field == zero { s.Field = default }) that resets it is the bug shape. Webhook-admission bypass without touching the admission layer at all. - Black-box probe (authorized clusters only, RBAC-scoped): with a lowest-tier principal that can create
generators.external-secrets.io/v1alpha1 Webhook, reference a canary Secret without the label. Success = the label predicate is not running; the exfil proof is your own owned-listener URL receiving the canary value — never a real secret. Two-tenant decision table: labelled vs unlabelled target Secret × your principal vs control principal. - Generalize to any controller/operator CRD that gates resource references by label/annotation: the reference-target label check is a first-class authz surface. Test create-leg with an unlabelled target before assuming the admission policy or controller-side check fires — same add-leg-missing-update-leg's-guard genealogy as the Oct 3 Groundhogg upsert leg.
October 7 follow-up: Elasticsearch cross-cluster search authorizes one identifying attribute while a second, independently supplied one selects the shard (CVE-2026-103009 / GHSA-jh46-59x3-g5qh, high) + delegated role-management scope not enforced (CVE-2026-103007 / GHSA-5jrv-cmh5-3vmg)¶
The page's invariant in its purest two-attribute form: in the RCS 2.0 cross-cluster search path, the authorization check validates one identifying attribute of the target shard while a separate, independently-supplied attribute in the same request determines which shard is actually accessed — a crafted CCR request references an unauthorized remote shard through the second field and reads it. Companion leg: a delegable, non-default role-management privilege scoped to specific indices fails to account for a role-definition setting that expands the matched index set, so the delegated scope the grantor believed they bounded is wider in enforcement than in grant.
- Sweep rule past this page's path-vs-query and header-vs-body legs: for any request whose target is described by more than one identifier field (cluster+shard, tenant+object, project+resource, path+query-param), diff which field the authz check consumes against which field the data layer consumes. The Convoy single-item-get leg proved the degenerate case (one field ignored); Elasticsearch proves the split-consumption case (each field used by a different layer). Operator probe on any search/CCR-style API with a second-scoped cluster/tenant: hold a legitimately-authorized pair and swap only the non-checked attribute to a canary target in another tenant.
- The delegated-privilege leg generalizes grant-vs-enforcement scope drift: when an admin-scoped capability is delegable with a restriction, test the restriction against every setting that can widen the matched set (wildcards, aliases, index patterns, role settings the grantor cannot see) — the delegate's effective reach is what enforcement resolves, not what the grant UI displayed. Lab clusters, synthetic roles/indices only.
October 7 21:3xZ follow-up: OpenStack Zaqar WebSocket transport never binds the authenticated project to subsequent requests (CVE-2026-107363 / GHSA-hc4w-8h6h-frmg, medium, fixed 23.0.1)¶
The page's invariant on a stateful transport: Zaqar authenticates the Keystone token at WebSocket connect, then fails to bind the project identifier in subsequent requests to the authenticated project — any client holding a valid token for project A substitutes project B's UUID in in-protocol messages and enumerates, inspects, creates, or deletes B's queues. Only WebSocket-transport + Keystone deployments are affected; the REST path enforces the binding the WebSocket path drops.
- Durable rule: run the tenancy harness per transport, not per product. The page already carries the per-listener auth rule (process-compose MCP leg) and per-grant-type rule; this adds per-transport-state: when a product exposes both a stateless HTTP API and a stateful WebSocket/message-bus channel for the same resource family, the auth context established at handshake is the easy place to lose scope binding because each in-protocol message re-supplies the target identifier. Probe shape: authenticate with a lowest-tier project token, then replay the same subscribe/publish/list message shapes with a foreign project UUID substituted into the payload field and diff the response.
- Generalizes past OpenStack: every protocol where auth happens once at connect and every subsequent frame carries its own scope field (STOMP destinations, MQTT session topics, GraphQL subscription payloads, JSON-RPC params after auth handshake, SSH-channel opens after userauth) is in scope for a scope-substitution battery. Cross-ref the Oct 2 UTMStack STOMP leg — same "auth lives at the handshake, scope lives in the frame, the frame wins" shape.
- Validation boundary: two disposable Keystone projects, synthetic queue-name markers, delete proofs limited to queues you created.
October 8 09:31Z follow-up: Apache DolphinScheduler authorization wave — the scope parameter is checked, the resource is not, and "unauth-" is a route-family fingerprint (7 GHSAs, fixed 3.4.3)¶
A coordinated DolphinScheduler wave (CVE-2026-66082/66084/66087, CVE-2026-71183/71895/71896; all fixed 3.4.3) restates this page's core invariant across an entire scheduler API with three sharper operator rules.
- "Authorized on some project" is the only precondition — supplied-scope-vs-owned-scope confusion. The schedule online/offline and workflow-definition release legs (CVE-2026-66082 / GHSA-r995-5488-jh52), task-definition modify (CVE-2026-66084 / GHSA-26vh-p5jc-q2mg), and task-instance stop/savepoint (CVE-2026-66087 / GHSA-8x8j-rj29-7qph) all check permissions against the
{projectCode}you supply, then act on a resource ID from any project — the handler never verifies the target belongs to the scope it authorized. This is the Convoy list-vs-get leg generalized: the check isn't missing, it's applied to the wrong binding. Sweep rule: on any path like/{scope}/{action}/{resourceId}, you must hold some valid scope to get past the gate — provision the lowest-tier membership you can, then replay every verb with foreign-scope resource IDs. State-change verbs (online/offline/release/stop/savepoint) are the tested-rarely set; the advisory set proves they are where implementers skip the ownership predicate. - Endpoints literally named
unauth-*are self-documenting recon. CVE-2026-71183 / GHSA-g89m-rq7v-96f2:/unauth-datasourceand/authed-datasourcereturn data-source connection info including passwords to any authenticated user without permission checks. Route-name fingerprint: grep JS bundles and openapi dumps forunauth,public,open,check,testprefixes — paths named for "no auth needed" frequently skip authorization checks even when reached authenticated, because the name reflects an original guest-facing design the permission layer was never backported to. Credential-relay legs like this outrank the tenancy legs in reporting: a low-priv scheduler account becomes direct database access with the platform's own saved credentials. - Admin-shaped config endpoints behind non-admin auth are standing cluster-credential oracles. CVE-2026-71895 / GHSA-vpj2-fvw3-8jgc: 3.2.0–3.4.2 let any authenticated non-admin retrieve the Kubernetes kubeconfig intended for admin-managed cluster config — bearer tokens/client certs that authenticate directly to the Kubernetes API, outside the product's own RBAC. Audit rule for any platform with a K8s/cloud integration: enumerate every endpoint that stores cluster credentials (kubeconfig, cloud profiles, agent bootstrap tokens) and check the read verb's role gate specifically — write/update legs get admin gates, read/display legs drift. The pivot the report should name: scheduler account → kubeconfig →
kubectlagainst the cluster with whatever SA permissions the config carries (cluster-admin ⇒ read Secrets, create pods, persist). - Also in the wave:
/users/list-allreturns other users' account info without the permission check (CVE-2026-71896 / GHSA-q59v-7f6x-gwrc) — account-enumeration class, fold into the same tenancy sweep. Harness: two disposable projects in two tenants, synthetic datasource passwords and a lab kubeconfig with canary tokens; prove read of the foreign canary, stop before using disclosed credentials against anything you do not own.
October 10 follow-up: forged identity headers and default-open webhooks — the "who is this caller" answer comes from the caller (AstronRPA pair, cc-connect, Immich partner sync)¶
- AstronRPA ≤ 1.1.6 OpenResty gateway accepts ANY Bearer value (GHSA-fh8f-pxvw-9f29 / CVE-2026-108548, medium):
auth_handler.luachecks the presence of anAuthorization: Bearerheader, not its validity, then downstream services trustX-User-Id/user_idheaders for identity — unauthenticated attacker picks any user by editing a header to reach/api/resource/and/api/rpa-ai-service/. Companion leg (GHSA-mcqv-c7v8-6mw4 / CVE-2026-108547, high):get-batch-shared-varhas no tenant check — enumerate sequential shared-variable IDs and decrypt other tenants' "all-users" variables (they get re-encrypted under your tenant key) to recover their credentials in plaintext. Battery additions: (1) presence-only bearer probe — sendAuthorization: Bearer garbageagainst every gateway-fronted route family; a 2xx/403-from-app (not 401-from-gateway) means the gate is a string test. (2) identity-header grep — on any RPA/automation/workflow platform, sweep downstream services for identity taken from headers the edge is supposed to set (X-User-Id,X-Tenant-Id,X-Auth-*): reach the service port directly, or send the header through a gateway that doesn't strip client-supplied copies. (3) "re-encrypted with your key" is a cross-tenant decrypt oracle fingerprint — any feature that hands you ciphertext it claims other tenants also use is worth a decode attempt with your own credentials. - cc-connect ≤ 1.5.0 MAX adapter webhook runs privileged commands for forged admin users (GHSA-h8j8-r6g6-37hc / CVE-2026-108549, critical): webhook mode on port 8080 accepts unauthenticated updates when
webhook_secretis unset (the default), and the payload'suser_idfield selects command authority — forge an admin user_id and/shellexecutes on the host. Recon pattern for every chat/bot bridge product (cc-connect, ntfy-style relays, messaging adapters): (a) POST a syntactically valid update with no signature to the listener — silence/accept on unsigned payloads is the finding; (b) check whether authority comes from a payload field naming a user rather than from verified sender identity; default-off secret = fingerprint the config name from the bundle/source and test both states. - Immich ≤ 3.3.1 partner sync stream leaks Locked Folder asset metadata (GHSA-4jh4-m6r3-369v / CVE-2026-108165, medium):
POST /api/sync/streamwithPartnerAssetsV2/PartnerAssetExifsV1returns GPS, capture time, camera details for Locked-visibility assets because the sync queries never re-apply the visibility exclusion the main album/photo queries enforce. Generalizes this page's rule to parallel read channels: every product that grows a sync/export/search/index side-channel re-implements reads without the primary path's filters. Sweep: for each privacy feature (locked, hidden, archive, blur, trash), find the other endpoints that return the same object type (sync streams, exports, thumbnails, search, feed/ActivityPub) and check the exclusion is enforced there too. Boundaries: two disposable lab accounts with an accepted partner link and synthetic locked assets with seeded fake EXIF; never read real users' location data.
October 15 follow-up: authenticated-elsewhere means scope-never-checked (Vert.x ChainAuthHandler) and empty-allowlist means allow-anyone (holaOS channel-gateway)¶
- Eclipse Vert.x Web
ChainAuthHandlerskips ALL scope validation for pre-authenticated users (GHSA-wrr9-rmv8-g28m / CVE-2026-102460, high; vertx-web [4.0.0–4.5.34], [5.0.0–5.0.12], [5.1.0–5.1.9], 5.2.0): when a preceding handler in the pipeline has already authenticated the routing context,ChainAuthHandler.postAuthentication()relies on internal state that is only set during theauthenticate()phase — which is skipped entirely for pre-authenticated users. Inany()mode the missing state causes fall-through without delegating scope checks to any handler; inall()mode scope checks are unconditionally skipped. A valid token with insufficient scopes accesses scope-protected resources, with the most common trigger being overlapping handlers on nested routes: parent route authenticates broadly, child route's chain enforces scopes that silently never run. This is the page's invariant in phase-carried-state form: the scope enforcement layer consumes state produced by a different execution phase, and an alternate entry into the handler (already-authenticated context) bypasses the phase that sets it. Sweep rules: (1) any auth component that has a "user already authenticated" fast path is a scope-bypass candidate — grep handler implementations forisAuthenticated/user() != nullearly-returns before permission/scope checks; (2) black-box probe on any Vert.x (or chain/cascade-style) API: mint the lowest-scope token you can get issued, first hit a route the parent handler authenticates without scope requirements, then hit the scope-gated sibling/child route on the same session/context and diff against a cold-token request that wasn't pre-authenticated — cold-vs-warm auth-context parity is the decision table; (3) fingerprint: route trees with a broadPath.match("/api/*")authenticator plus per-routewithScope()chains are the exact advertised configuration that fails. Fixed versions: 4.5.35 / 5.0.13 / 5.1.10 / 5.2.1 — version band doubles as fingerprinting. - holaOS ≤ 2026.822.1 channel-gateway connectors accept every sender when
allowFromis empty (GHSA-v8pm-qrjg-7q78 / CVE-2026-110769, high; companion GHSA-7c2w-92qq-g97q / CVE-2026-110768 authorization bypass on the same connector family): the#isAllowedcheck accepts all senders when the defaultallowFromlist is empty, so anyone who can message the operator's Telegram/Slack/Discord bot hasChannelIngress.handlepass turns to the workspace agent, running configured tools on the operator's machine. This extends the cc-connect leg above from "unsigned webhook accepted when secret unset" to the broader empty-collection-means-allow-all default: an allowlist whose default state is empty is a fail-open configuration, not a deny-by-default one. Sweep rule for the whole agent-bridge/bot-connector class (holaOS, cc-connect, ntfy relays, MCP-over-chat gateways, any "message the bot to run tools" product): fingerprint the allowlist config key from source/bundle, then test three states — unset/empty, contains-your-id, contains-someone-else's-id — and diff sender acceptance. An empty list that admits traffic is the finding; the privilege jump here is chat-message → arbitrary tool execution on the operator's host, so treat any reachable bot identity as a remote-code surface for agent products. Boundaries: owned bot accounts on owned channels, inert canary tool only.