Quarkus, OpenMRS, and Incus path/import boundary batch (GHSA)¶
Signal: GitHub Security Advisories Atom surfaced five durable advisories on 2026-05-04 covering URL path authorization differentials, archive/path traversal, unauthenticated file read, and authenticated import-triggered daemon crashes.
Advisories in this batch¶
- Quarkus matrix-parameter authorization bypass —
io.quarkus:quarkus-vertx-httpcan authorize one path while RESTEasy Reactive routes another after stripping semicolon matrix parameters. Example pattern:/api/admin;anythingmay bypass policies protecting/api/admin. Fixed in 3.20.6.1, 3.27.3.1, 3.33.1.1, and 3.35.1.1 depending on release line. References: https://github.com/advisories/GHSA-rc95-pcm8-65v9, CVE-2026-39852. - OpenMRS module upload Zip Slip / arbitrary file write —
org.openmrs.web:openmrs-webversions<= 2.7.8and2.8.0-2.8.5allow authenticated module upload archives to write outside the intended module directory; writing JSPs under the webapp root can become RCE. No patched version was listed for this advisory at scan time. Reference: https://github.com/advisories/GHSA-78fc-9688-w8xw, CVE-2026-40076. - OpenMRS unauthenticated module resource path traversal —
ModuleResourcesServletcan serve arbitrary local files through module resource paths on affected OpenMRS deployments; OpenMRS2.8.6fixes the2.8.xline, while<= 2.7.8had no patched version listed at scan time. Exploitability depends on older Tomcat path-parameter handling for the published bypass. Reference: https://github.com/advisories/GHSA-jjgj-cx3q-pw4w, CVE-2026-40075. - Incus custom volume backup import nil-pointer DoS — authenticated users with custom volume import access can crash
incusdusing craftedindex.yamlvolume snapshot metadata. Fixed in 7.0.0. Reference: https://github.com/advisories/GHSA-r7w7-mmxr-47r9, CVE-2026-40197. - Incus storage bucket import nil-pointer DoS — authenticated users with storage bucket import access can crash
incusdusing malformed bucket backup metadata. Fixed in 7.0.0. Reference: https://github.com/advisories/GHSA-gc7j-g665-rxr9, CVE-2026-40195.
Why this is durable¶
The recurring failure is that string or archive structure was trusted before being resolved into the thing policy actually protects.
- Path authorization must happen on the same canonical route key the application will dispatch.
- Archive extraction must validate the normalized destination path, not only the entry prefix.
- Static file handlers must prove the resolved file stays under an intended base directory.
- Import paths must reject absent/null metadata before dereferencing it in privileged daemons.
Immediate triage¶
- Find affected components: search SBOMs and manifests for
io.quarkus:quarkus-vertx-http,org.openmrs.web:openmrs-web, andgithub.com/lxc/incus/v6/cmd/incusd. - Patch quickly: move Quarkus to the fixed release for the deployed line, OpenMRS
2.8.xto2.8.6or later where available, and Incus to 7.0.0. - Constrain risky endpoints while patching: restrict OpenMRS module upload to trusted administrators from trusted networks; disable or proxy-block module upload REST paths if not required; restrict unauthenticated access to module resource paths where compatible with the deployment.
- Normalize before policy: for Java/HTTP services, test whether semicolon path parameters, encoded separators, and dot segments are treated identically by auth middleware, routers, and containers.
- Harden extraction/import: reject archive entries after
normalize().startsWith(allowedBase)fails; reject null/missing metadata blocks before import side effects start.
Hunt ideas¶
- Query access logs for
;in protected Quarkus paths, especially requests that returned2xx/3xxagainst admin/API routes. - Search OpenMRS logs for module uploads through
/ws/rest/v1/module, newly written files under webapp roots, JSP files with recent mtimes, or archive entries containing../after a valid prefix such asweb/module/. - Look for unauthenticated OpenMRS requests to
/moduleResources/containing dot segments, semicolon path parameters, URL-encoded traversal, or unexpected module IDs. - Inspect Incus logs for
panic: runtime error: invalid memory address or nil pointer dereferencenear storage volume or bucket import operations; correlate with user/project/storage-pool activity.
Durable controls¶
- Build path-policy tests that exercise raw path, decoded path, framework route path, servlet/container path, and filesystem path as separate values.
- Prefer deny-by-default module administration: no public module upload surface, explicit admin role checks on every API path, and audit logs for module install/update/delete operations.
- Treat uploaded archives as hostile data structures. Validate type, size, compression ratio, entry count, symlinks, hardlinks, absolute paths, normalized destinations, and metadata schema before extraction/import.
- Put daemon import operations behind authorization, quotas, crash containment, and rate limits; a single malformed import should not repeatedly take a control plane offline.
Operator lesson¶
When auditing paths, ask: “What exact string did auth approve, what exact object did the router/filesystem/daemon use, and what transformations happened between them?” Bugs live in that gap.
October 8 follow-up: RESTEasy SourceProvider XXE — the JAX-RS body-type handler your framework picked for you (CVE-2026-17615 / GHSA-h82j-45qh-f65r, high)¶
RESTEasy's SourceProvider.writeTo()/read side constructs a SAXParser with default features, so any endpoint whose resource method accepts application/xml into Source/StreamSource resolves external entities — unauthenticated remote file read into the HTTP response body. Published Aug 31, enriched into the updated feed Oct 8; fixed per advisory branch.
Why this earns a fold onto this page's Quarkus/RESTEasy lineage:
- XXE in a framework message-body reader is everywhere at once. The vulnerable parser is not in any application file — it is the provider the framework auto-selects when a resource method's parameter type is
Source/StreamSource/DOMSource. A single library fix covers N deployments, and conversely: fingerprinting an app fororg.jboss.resteasyjars plus anyjavax.xml.transform.Sourceparameter (grep-able in decompiled classes or via error/behavior probes) tells you the surface exists before you send a payload. Same "the framework's default choice is the boundary" theme as this page's matrix-parameter route split. - Type-based XXE probe battery: for every XML-accepting endpoint, POST the same body under
Content-Type: application/xmlwith each of the parameter shapes frameworks accept — the genericSource-typed leg is the one that bypasses app-level hardening, because app-levelXMLInputFactoryhardening only covers the readers the app constructs. Classic external-entity canary (<!DOCTYPE x [<!ENTITY e SYSTEM "file:///etc/passwd">]>in a benign element) against your own lab RESTEasy deployment is the proof; against third parties, use a parameter-entity/timeout-blind shape or an owned HTTP callback and stop at resolution proof. - Sweep extension for any Java stack: enumerate which
MessageBodyReaders are registered (RESTEasy providers, Jackson XML, JAXB, Digester) — each is its own parser with its own entity settings, and hardening one says nothing about the others. This is the multi-backend guard-parity rule restated inside a single framework.
October 12 follow-up: JAXB FEATURE_SECURE_PROCESSING was the only XXE control — a single-flag security assumption that fails per-JAXP-implementation (CVE-2026-92877 / GHSA-j929-4rw7-84fh, high, fix PR eclipse-ee4j/jaxb-ri#2013)¶
JAXB's factory methods (XmlFactory.createParserFactory, in both jaxb-ri/core and jaxb-ri/xsom) relied solely on FEATURE_SECURE_PROCESSING to prevent external-entity resolution. FSP is a limiting feature, not a blocking one — on JAXP implementations where FSP alone doesn't disable external entities (alternate SAX providers, some vendored/repackaged Xerces), unmarshalling untrusted XML through JAXB = OOB XXE local-file read. The fix diff is the operator lesson: the patched code sets the four explicit features — disallow-doctype-decl, external-general-entities, external-parameter-entities, load-external-dtd — instead of trusting one umbrella flag.
- Single-flag security assumptions are per-provider contracts, not guarantees. "We enabled FEATURE_SECURE_PROCESSING / secure processing / a hardened
XMLInputFactory" proves nothing about the JAXP implementation actually resolved at runtime (SAXParserFactory.newInstance()follows the ServiceLoader/META-INF discovery order — a jar on the classpath can silently swap the provider). Audit pattern: enumerate the resolved factory class name at runtime (log/printfactory.getClass()), not the configured features, then probe with an external-entity canary against your own lab deployment. - Fix-diff fingerprint for sweep timing: deployments still carrying a JAXB build predating PR #2013 are XXE-probable wherever untrusted XML reaches
unmarshal()— grep the app's XML-accepting endpoints for JAXB-bound parameter types, then send the same entity-canary battery per Content-Type. This leg generalizes the Oct 8 RESTEasySourceProviderleg directly above it: the vulnerable parser was a library default, invisible in application code, and app-level hardening never covered it. - Both legs are the same hunt: framework/library-constructed parsers are a separate inventory from app-constructed parsers — enumerate both, probe both, and treat any single-boolean "secure" setting as a claim to test, not a control to trust.
October 12 21:3xZ follow-up: Quarkus REST Data panache — customizing one generated operation silently DROPS class-level security (CVE-2026-87744 / GHSA-jh67-c59w-8h9x, 9.1 critical)¶
Quarkus REST Data with panache generates REST endpoints from entity/repository interfaces, and security normally comes from class-level @RolesAllowed/@Authenticated annotations fanned out to every generated method. The generator's defect: when a developer customizes a specific operation with @MethodProperties (name/description/OpenAPI tweaks) without re-declaring the security annotations on that method, the code generator emits the customized operation without the class-level security requirements at all — the affected endpoint defaults to permissive, unauthenticated access. One cosmetic OpenAPI edit turns a protected CRUD route into an open data endpoint, and the developer never sees the security annotation disappear because it was never written at method level in the first place.
- Code-generated surfaces inherit security by generation rule, and generation rules have override branches. The class-level-annotation-to-every-method fan-out fails exactly on the branch where a method gets any per-method customization — the generator treats "method carries its own annotation block" as "method brings its own security." Sweep rule on any annotation-driven generator (Quarkus REST Data/panache, Spring Security annotation scanning, JAX-RS sub-resource generation, OpenAPI codegen with security schemes): for each generated route family, find the customized operations first and test auth on those before the untouched siblings — they're the ones whose inheritance is in question. Black-box shape: unauthenticated
GETon generated entity collection paths with a 200-vs-401 diff against sibling non-customized entities. - Differential rule: generated-route inventory vs declared-security inventory vs runtime gating. Pull the OpenAPI document (
/q/openapion Quarkus, frequently itself unauthenticated) and diff which operations advertisesecurityrequirements against what actually gates at runtime. Spec-says-secured / server-says-200 = enforcement dropped at generation. This page's RESTEasy and JAXB legs established framework-constructed layers as a separate inventory from app code; this adds generated-from-your-code as a third inventory — app, framework, and generator each enforce (or not) independently. - Fingerprint: Quarkus-shaped banners + REST Data extension + any entity whose generated route answers unauthenticated; white-box grep is one pattern (
@MethodPropertieswithout adjacent security annotations). Silent-security-drop report framing, same family as the JAXB single-flag leg directly above: the control exists somewhere else and nobody verifies it reached the enforcement point. - Validation boundary: own lab app with a disposable entity; prove 401-on-sibling vs 200-on-customized with synthetic rows; never enumerate third-party deployments' generated endpoints beyond owned scope.