Vulnerabilities
The Semicolon That Walked Past Quarkus Authorization (CVE-2026-39852)
Quarkus HTTP security once matched permissions against a path carrying matrix parameters while RESTEasy dispatched the same resource by its base path; production should run a supported maintenance release with the June full-normalization repair and accept it with policy, destination, side-effect, and running-artifact evidence.

In this article
An anonymous GET /api/admin received 401 Unauthorized on an affected Quarkus application. Changing only the request target to GET /api/admin;bypass let the same Jakarta REST resource return 200 OK. The caller supplied no credential and selected no explicit permit rule. The HTTP security matcher retained ;bypass and found no permission configured for that decorated string. RESTEasy then parsed the semicolon content as segment parameters and selected the resource whose base segment was admin.
A matrix parameter sits inside a path segment. In /catalog/items?state=open, state belongs to the query component as a whole. In /catalog/items;state=open, the value is attached to the items segment. Jakarta REST can route the latter using the base path /catalog/items while exposing state=open to the resource method. CVE-2026-39852 arose when authorization and resource dispatch consumed those two representations at different stages.
By July 26, 2026, the production decision had moved beyond the May first-fix releases. Quarkus published CVE-2026-50559 in June for encoded and multiply encoded path discrepancies, with HttpSecurityUtils.normalizePath() providing the complete primitive. New deployments should default to the current 3.33.2.1 release on the officially recommended 3.33 LTS line; existing 3.27 LTS deployments need at least 3.27.4.1; teams following the latest non-LTS line should use 3.37.4. Recheck the official release ledger before change approval. A build containing only May's pathWithoutMatrixParams() no longer satisfies current acceptance.
1 The current repair target is a supported stream with the full normalizer
This upgrade is a permanent requirement. Ingress filtering can narrow the exposure window, and method annotations may preserve an independent authorization boundary. The old runtime still uses different destination identities across its path consumers. The change ticket needs a target release, build provenance, every ingress, policy-to-resource comparisons, an approved rollback build, and the conditions for withdrawing temporary rules.
1.1 Code location and response closure
1.2 First-fix releases and current acceptance releases serve separate records
The May bulletin answers when each stream first repaired CVE-2026-39852's literal-matrix discrepancy. The June bulletin answers when the encoded-path follow-on reached each stream. The release ledger and security policy answer which streams still receive maintenance commitments today. Keeping those three clocks in one decision table prevents a historical floor from becoming a current production target.
| Stream | May literal-matrix first fix | June full-normalization floor | Current production action |
|---|---|---|---|
| 3.20 LTS | 3.20.6.1 | 3.20.6.2 | Community maintenance has ended. Accept only under explicit vendor support with equivalent evidence; otherwise migrate to a supported stream. |
| 3.27 LTS | 3.27.3.1 | 3.27.4.1 | Still supported. Existing deployments need at least 3.27.4.1 plus final-artifact and running-instance evidence. |
| 3.33 LTS | 3.33.1.1 | 3.33.2.1 | The officially recommended production LTS. Stability-oriented new deployments default to its current micro release. |
| 3.34 / 3.35 | 3.34.7 / 3.35.2 | No June floor was released on these streams | Both community streams have ended. Migrate to a supported line and do not retain the May first fix as a long-lived baseline. |
| 3.36 | Inherited by the stream | 3.36.3 | The release ledger still lists this micro, while the community security commitment names the latest 3.x. Prefer migration to 3.37 or document explicit vendor support. |
| 3.37 | Inherited by the stream | 3.37.0 | The current micro on the latest non-LTS stream is 3.37.4. Deployments choosing this stream should use that current micro. |
The GitHub Advisory Database once represented one affected interval as >=3.34.0,<3.35.1.1. A cross-minor numeric interpretation would mark 3.34.7 affected even though the Quarkus May bulletin lists it as repaired. Historical reconstruction should retain this upstream metadata conflict and use the project's branch release record for 3.34.7 and 3.35.2. Current production approval uses the June full-normalization floors and today's support status, leaving the historical conflict outside the critical upgrade path.
2 Authorization retained the decoration before dispatch restored the base path
The minimal configuration maps the authenticated policy to /api/admin with quarkus.http.auth.permission.admin.paths=/api/admin and quarkus.http.auth.permission.admin.policy=authenticated. A plain anonymous request matches that entry, so Quarkus selects a permission checker and initiates an authentication challenge. The decorated request changes the runtime value to /api/admin;bypass, causing the exact-key lookup to miss.
Prefix lookup also misses. In the affected commit, ImmutablePathMatcher.match() at lines 54–84 tries exact lookup first. When a candidate prefix is shorter than the request, the following character normally has to be a slash. That boundary keeps a policy for /api/admin away from /api/administrator, while a semicolon immediately after admin also falls to the default result. RESTEasy later parses the same exchange, retains ;bypass as segment data, and dispatches using /api/admin.
2.1 An empty permission list reached the public-route base case
findPermissionCheckers() converts only matched records into HttpSecurityPolicy instances. The decorated path produces no record, leaving a list of length zero. doPermissionCheck() begins at index zero and immediately satisfies index == permissionCheckers.size(), returning a permitted result. The code writes PATH_MATCHING_POLICY_FOUND only when the index is greater than zero. Reliable telemetry records the checker list, ordered permission identifiers, and final decision; these fields also expose an overlapping configuration that selected a weaker rule.
The empty-list base case supports intentionally public routes. Quarkus retained that behavior and repaired the classification of protected destinations. Turning every empty list into denial would change the default for every uncovered route and turn a path-identity flaw into a wider compatibility break.
request target /api/admin;bypass
security comparison value /api/admin;bypass
configured policy key /api/admin
permission checkers empty
HTTP security result continue
RESTEasy base path /api/admin
resource result admin method selected
The same ledger handles overlapping policies. A deployment might protect /api/admin with a strict role rule and map the broader /api/* expression to a weaker policy. A decorated representation can miss the narrow entry and select the broader one. The response may succeed and the list may be nonempty. Acceptance has to assert the exact permission name and ordering before checking the selected resource and method.
2.2 RESTEasy wrote the base path after the security decision
MatrixParamHandler detects a semicolon and calls initPathSegments(). That method splits the path at slashes, builds a PathSegmentImpl for each nonempty component, and assembles a new path from each segment's getPath(). The request /api;a=1/admin;b=2 consequently participates in resource matching as /api/admin, while both parameter maps remain attached to their respective segment objects for application use.
Order determines the consequence. Quarkus HTTP security completes permission selection first. RESTEasy generates the base path later; by the time that write occurs, the HTTP security decision is complete. A log pipeline that retains only the CDN target or only the resource template splits one request into two apparently unrelated names. Investigation and acceptance need the raw target, Vert.x path, security comparison value, permission, REST template, and Java method under one correlation identifier.
Legitimate matrix traffic belongs in compatibility acceptance. A correct-role request to /orders;view=open should reach the same resource method as the plain path while retaining view=open. Anonymous and wrong-role identities should be denied by the same policy for both forms. Security and functional results must coexist in the same request record, showing that the repair aligned resource identity while preserving business semantics.
3 Policy placement, downstream consumer, and residual checks define exposure
Affected-version presence begins prioritization. Material exposure also requires the deployment to use path-based HTTP permissions or another affected path consumer; at least one ingress must deliver the relevant representation; the security comparison must miss the intended rule or select a weaker rule; a later consumer must resolve that representation to a protected target; and no independent authorization check may stop the caller before the sensitive operation takes effect. Running configuration, request traces, and resource evidence can answer every one of these questions.
Independent method checks such as @RolesAllowed and @Authenticated still execute at resource invocation. They reduce actual impact while the HTTP path policy remains vulnerable. Test anonymous, authenticated wrong-role, and correct-role identities separately. The reachable caller population follows the skipped policy—authentication, role, or custom policy—and the controls that remain.
Business consequence follows the resource method. For a read route, determine which records, cache entries, and audit events were returned. For a write route, inspect database transactions, messages, webhooks, issued credentials, and configuration changes. For an administrative route, trace durable sessions, accounts, and keys. Public source establishes the authorization-bypass mechanism. Deployment-specific method capability, verified in-the-wild exploitation, and victim loss require resource and side-effect evidence from the affected environment.
3.1 Twenty-three changed files reduce to four path-sensitive roles
The representative May repair changes twenty-three files. That unit is modified files from a pinned repository review. Application exposure depends on the path-sensitive roles it actually enables. Grouping the files by security decision produces four operational roles. Teams can exclude disabled extensions explicitly and verify every enabled role on its own terms.
| Consumer | Path-based decision | Repair evidence | Deployment verification |
|---|---|---|---|
| Vert.x HTTP permission matcher | Selects checkers for quarkus.http.auth.permission.*.paths | May removes matrix content first; June canonicalizes both configuration and request values | Export real permissions, method limits, and overlap order; assert one permission for every equivalent form |
| Keycloak policy enforcement and OIDC static tenancy | Chooses a policy-enforcer route or authentication tenant | The representative commit applies the helper to the matcher and associated request facade | Record selected tenant, policy, principal, and resource; test every multitenant ingress |
| CSRF, servlet, and management security | Chooses token handling, servlet permission, or management authentication | Commit call sites and extension regressions cover these roles | Exercise only enabled stacks and preserve method, outcome, and side-effect comparisons |
| RESTEasy resource dispatch | Parses segment parameters and selects the base resource | Affected source writes the rebuilt path back to the request context | Confirm plain and matrix forms select one template while values remain on the correct segment |
The evidence layers remain distinct. Project bulletins define release versions and recommended upgrade actions. Commit-pinned source directly defines functions and state changes. The twenty-three-file grouping is repository-derived analysis. The three-identity canary, ingress matrix, and side-effect ledger are local acceptance designs proposed here. Running digests establish old-container retirement, per-layer request fields establish proxy forwarding, and correlated logs, permissions, destinations, and side effects establish historical unauthorized access.
4 The May patch handled literal matrix syntax; June unified full paths
Quarkus authored the representative first repair on April 7, 2026 and released the CVE-2026-39852 builds on May 4. Its contract was deliberately narrow. pathWithoutMatrixParams() recognizes literal semicolons and slashes, returns ordinary paths directly, and removes each segment-local matrix region while preserving segment boundaries. RESTEasy continues to expose matrix values to resource methods.
The follow-on commit authored May 11 expanded the security comparison contract, and Quarkus released the CVE-2026-50559 floors on June 17. Full normalization repeatedly percent-decodes until the value stops changing, then handles matrix content, null bytes, backslashes, and dot segments. That sequence exposes %3B and %253B before matrix removal and compares configured and request paths at the same canonical stage.
4.1 The May helper preserved segment structure
The function enters a matrix region when it sees ; and resumes copying only at the next /. Both /a;/b; and /a;a1=1;a2=2/b;b1=1 become /a/b. Repeated slashes, trailing slashes, and empty segments remain available to the downstream matcher. This narrow contract explains the original defect and makes a symbol check for the helper evidence of only the May stage.
The representative repair also adds a configuration guard. A literal semicolon in an ordinary path permission raises ConfigurationException. Applications intentionally incorporating matrix values into authorization need to express that meaning in a custom HttpSecurityPolicy, with method authorization supplying an independent boundary. Searching path-permission configuration for literal semicolons before upgrade turns a startup failure into a planned migration.
Upstream tests separate function semantics from request outcomes. Utility cases cover bare semicolons, parameter names, key-value pairs, multiple segments, extra equals signs, and slash structure. RESTEasy Reactive, RESTEasy Classic, and affected extensions exercise protected routes with anonymous and authenticated identities. Backport acceptance records the actual test classes, platform BOM, extension versions, and build digest; together they establish complete consumer coverage.
4.2 Full normalization moves encoded forms into one comparison stage
normalizePath() invokes the decoder while a percent sign remains and stops when one pass produces no change. It then removes matrix content, removes null bytes, converts backslashes to slashes, and resolves dot segments in a fixed order. Maintained regression tests cover upper- and lower-case encoded semicolons and nested encoding while retaining consumer-specific expectations for REST and static resources.
The follow-on investigation found encoded and multiply encoded semicolons capable of extending the original path-identity discrepancy. Encoded slash and backslash formed a separate exploitable path in the related protected static-resource consumers. Unreserved-character encoding, %00, encoded dot segments, and REST %2F/%5C remain regression negative controls. Those controls preserve results for the investigated consumers; detection pipelines should keep them separate from confirmed bypass forms.
Applying the normalizer to both configuration and request values is central to the June repair. Cleaning only the request leaves configuration aliases; cleaning only configuration leaves runtime aliases. Acceptance should also check idempotence: a canonical value produced by the maintained implementation should not reveal a second destination when passed through the same implementation again. A second transformation indicates an incomplete backport, proxy pipeline, or test fixture.
5 Acceptance observes policy, destination, side effects, and running artifact together
Acceptance needs all four facts; a status code describes only one HTTP outcome. A CDN-generated 401 shows that ingress mitigation. A 404 for every decorated request can conceal a broken legitimate matrix API. A correct-role 200 can belong to a fallback page. The canary response should include a build-specific nonce, resource template, and legitimate matrix values, while the audit record retains the permission name, principal, and running digest.
5.1 One matrix covers equivalence, function, and deployment
| Input and identity | Policy outcome | Destination and function | Side-effect and deployment evidence |
|---|---|---|---|
| Anonymous or wrong role; plain and literal-matrix paths | Every form selects the same protected permission and is denied or challenged | The canary method does not execute and returns no build nonce | Database, queue, outbound, and audit state remain at the declared denial baseline; retain the instance digest |
Anonymous or wrong role; %3B, %3b, and nested encoding | Normalization selects the same protected permission | No protected method executes | Every ingress and controlled direct path passes; proxy transformations occupy separate fields |
| Correct role; plain and legitimate matrix forms | Both forms select the same permission and are allowed | One resource template executes and values remain on the intended segment | Only declared business or audit changes occur; the response nonce binds to an approved digest |
| Any identity; unrelated public route | Existing public or protected semantics remain | No accidental route collapse occurs | Pre- and post-upgrade function comparisons agree |
| Static-only positive candidates and REST negative controls | Run official consumer-specific expectations | Record static files and REST templates separately | Negative controls stay in the corpus and never become confirmed exploitation through pipeline relabeling |
Use one correlation identifier to collect the client raw target, edge record, forwarded request line, Vert.x value, canonical security key, permission, authentication mechanism, principal, tenant, REST template, Java method, status, nonce, and artifact digest for each row. If a proxy decodes or rejects input, retain the raw field and add derived fields. Destructive replacement of the original value removes the byte-level starting point for historical analysis.
Scale the side-effect ledger to the business route. A read-only canary records at least the audit-event sequence. Representative application tests may add synthetic database row counts, message counts, object writes, and mocked outbound calls. Anonymous and wrong-role variants leave the ledger unchanged. Correct-role variants produce only the declared changes. Destructive actions remain in isolated fixtures or transactionally disposable test data.
5.2 The rollback build keeps the full repair
Deploy first to a canary receiving the same proxy transformations and authentication integrations as production, then expand traffic. Run the same matrix through every region, alternate hostname, service-mesh route, management network, disaster-recovery ingress, and controlled direct path. Every instance behind the load balancer must carry an approved digest. Stale native images, manually pinned extensions, cached containers, and stopped images that can rejoin traffic require explicit retirement.
Write the rollback target into the release plan before rollout and pin it to another build containing the June full normalizer. Preserve the failed matrix row, proxy configuration digest, and response evidence, then repair compatibility on that security baseline. May-only and earlier versions stay outside the approved rollback set because they reintroduce published path representations.
Withdrawing the temporary ingress rejection rule is a release event. After all instances and rollback images pass, remove the rule under controlled traffic and replay literal and encoded matrix forms, legitimate business parameters, and unrelated public routes. Complete withdrawal when policy, resource, and side effects remain aligned. If a row fails, restore the temporary rule, preserve the failing input, and isolate the application or proxy discrepancy without reversing the runtime upgrade.
6 Historical review starts from protected destinations
Export runtime quarkus.http.auth.permission.*.paths entries, programmatic HttpSecurityPolicy implementations, method annotations, and enabled path-sensitive extensions. Map them to real REST, servlet, management, and static destinations. That inventory defines which requests deserve investigation. A global log search for semicolons mixes legitimate matrix traffic, ordinary URIs, and unrelated scanning noise.
Preserve raw request targets from the CDN, WAF, reverse proxy, service mesh, and application logs. Derive literal-semicolon, encoded-semicolon, and nested-encoding candidates in working copies. Treat static-resource encoded slash and backslash separately and retain the REST negative controls. Then correlate canonical security key, permission, identity, resource template, method, response size, and state change using a request ID, time window, connection metadata, or trace context.
6.1 Evidence narrows from candidate syntax to business impact
A raw target containing relevant syntax begins as a candidate. A derived canonical value mapping to a protected path makes it relevant. A relevant event with the expected permission absent or replaced by a weaker permission becomes a likely bypass. Joining that request to a resource method, response content, or protected side effect confirms the concrete impact. These stages keep search volume, authorization anomalies, and affected transactions in separate metrics.
Redirects and exceptions still require side-effect ordering. A redirect can change session state, an exception can follow a write, and an asynchronous job can outlive the response. Application audit records, database transactions, messages, object-store access, and downstream API calls define containment. When unauthorized execution is confirmed, revoke sessions or tokens issued by that method, rotate secrets it actually exposed, reverse configuration changes, review created accounts, and notify downstream data owners.
Keep legitimate matrix traffic in the evidence set with its resource, parameter purpose, authorized identity, and expected outcome. Durable detection focuses on a path-identity and authorization disagreement within one trace: a request resolves to a protected destination without the expected permission, or two security-relevant layers produce different base routes. A semicolon alone remains a candidate filter.
6.2 Closure binds running digests and remaining unknowns
Deployment closes only when the supported stream contains the June full normalizer; platform, extension, application, container, and running-instance identities are traceable; every ingress and rollback image passes the three-identity matrix; legitimate matrix behavior remains intact; protected side-effect counts for denied requests remain zero; and temporary rules are either withdrawn after verification or retained as owned, expiring exceptions. Any stale instance, stale image, or untested ingress keeps the conclusion open.
A service that cannot migrate immediately needs an exact version, vendor-support state, reachable routes, ingress transformations, remaining method checks, temporary rejection rules, monitoring fields, owner, and expiration. Keep it outside the approved digest set and replay compensating controls plus controlled direct access on every renewal. Reaching a June version floor establishes the code starting point; the running artifact and effective policy still require per-instance evidence.
The decisive fact in CVE-2026-39852 is compact: the security layer classified /api/admin;bypass outside the configured path, and RESTEasy later restored it to /api/admin. The complete repair moves equivalence before the security decision and extends the contract to encoded paths and related consumers. Production closure is equally concrete: every equivalent input selects one policy and destination, denied identities create no side effect, authorized identities retain legitimate behavior, and every running instance comes from an approved build.
Research record
7Evidence, objects, and sources
The material below preserves the identifiers and references used in this report.
7.1Research objects
Products, actors, techniques, affected objects, and control points discussed in the report.
Quarkus path-policy bypass through literal matrix parameters
GitHub advisory for the original matrix-parameter mismatch
Affected HTTP security package
Path-based permission family that requires route-by-route review
May matrix-only canonicalization implementation; other release streams use distinct backport SHAs
Repeated percent decoding, matrix removal, null-byte removal, slash normalization, and dot resolution before security matching
Published follow-on release floors; production uses the current maintenance release in a supported stream
Literal and encoded matrix representations that require correlation with a protected destination and identity mismatch
Encoded slash and backslash were exploitable only in the related protected static-resource consumers
Quarkus investigated these representations and confirmed no exploitable bypass in the named REST cases
7.2Event chronology
- Coordinated report
GitHub Security Lab reported the matrix-parameter authorization discrepancy to Quarkus.
- Initial repair authored
Quarkus authored representative 3.35-branch commit 361a287f530dfef8 with the matrix-only helper, cross-extension changes, documentation, and tests; other release streams received distinct backports.
- CVE-2026-39852 published
Quarkus published the bulletin and first fixed release lines for the literal-semicolon flaw.
- Technical advisory published
GitHub Security Lab published GHSL-2026-099 with the proof request and affected call chain.
- Follow-on normalizer authored
Quarkus authored commit fc5c83aab65f62a2 with the full path normalizer and regression suite.
- CVE-2026-50559 releases published
Quarkus published the encoded-path follow-on and new fixed release floors.
- Support status reviewed
The official pages list 3.33 and 3.27 LTS plus the latest 3.x as supported streams, with 3.37.4 as the current non-LTS micro.
7.3Sources and material
- Quarkus CVE-2026-39852 security bulletinhttps://quarkus.io/blog/CVE-2026-39852/
- Quarkus GHSA-rc95-pcm8-65v9 advisoryhttps://github.com/quarkusio/quarkus/security/advisories/GHSA-rc95-pcm8-65v9
- GitHub Advisory Database record for affected rangeshttps://raw.githubusercontent.com/github/advisory-database/main/advisories/github-reviewed/2026/05/GHSA-rc95-pcm8-65v9/GHSA-rc95-pcm8-65v9.json
- GitHub Security Lab GHSL-2026-099 technical advisoryhttps://securitylab.github.com/advisories/GHSL-2026-099_Quarkus/
- Quarkus initial matrix-parameter repair commithttps://github.com/quarkusio/quarkus/commit/361a287f530dfef8caf90e3048ba9294ed5b5d98
- Initial pathWithoutMatrixParams implementationhttps://github.com/quarkusio/quarkus/blob/361a287f530dfef8caf90e3048ba9294ed5b5d98/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityUtils.java#L47-L64
- Pre-repair HTTP path policy and empty-list branchhttps://github.com/quarkusio/quarkus/blob/240ec34c6257f7c410b9ab90f4f8976029062bd6/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/AbstractPathMatchingHttpSecurityPolicy.java#L122-L208
- RESTEasy Reactive MatrixParamHandler sourcehttps://github.com/quarkusio/quarkus/blob/240ec34c6257f7c410b9ab90f4f8976029062bd6/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/MatrixParamHandler.java#L6-L12
- RESTEasy Reactive path-segment initialization sourcehttps://github.com/quarkusio/quarkus/blob/240ec34c6257f7c410b9ab90f4f8976029062bd6/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java#L838-L875
- Quarkus web endpoint authorization referencehttps://quarkus.io/guides/security-authorize-web-endpoints-reference
- Jakarta RESTful Web Services specificationhttps://jakarta.ee/specifications/restful-ws/4.0/jakarta-restful-ws-spec-4.0.html
- RFC 3986 URI generic syntaxhttps://www.rfc-editor.org/rfc/rfc3986
- Quarkus CVE-2026-50559 follow-on bulletinhttps://quarkus.io/blog/CVE-2026-50559/
- Quarkus encoded-path follow-on advisoryhttps://github.com/quarkusio/quarkus/security/advisories/GHSA-qcxp-gm7m-4j5v
- Quarkus full path-normalization follow-on commithttps://github.com/quarkusio/quarkus/commit/fc5c83aab65f62a2d7c7c8f9fd6845b080f94f53
- Quarkus release streams and current maintenance statushttps://quarkus.io/releases/
- Quarkus security and supported-version policyhttps://quarkus.io/security/