Quarkus security checks
The Security panel, on Quarkus, runs a fixed, on-demand 45-rule ruleset against the host application's Quarkus security configuration — not Spring Security. It reads the effective quarkus.http.*, quarkus.oidc.*, quarkus.smallrye-jwt.*, quarkus.tls.*, quarkus.management.*, quarkus.http.proxy.*, quarkus.security.users.embedded.*, quarkus.rest-csrf.* (the CSRF extension), quarkus.grpc.server.*, quarkus.smallrye-graphql.*, quarkus-elytron-security-jdbc principal-query settings, and Kafka/SmallRye Reactive Messaging channel security settings, plus build-time counts of the standard authorization annotations (@RolesAllowed, @PermitAll, @DenyAll, @Authenticated, @PermissionsAllowed, and @AuthorizationPolicy) discovered in the application's own classes. It never intercepts live traffic, exposes credentials or secrets, or modifies the configuration. Findings are heuristic review prompts; the right remediation depends on the application's threat model and deployment topology.
OIDC checks aggregate the active default tenant and active named tenants; a tenant with quarkus.oidc[.<tenant>].tenant-enabled=false is excluded.
Reading more than the preview
Quarkus Security keeps its twenty-entry sampleViolations preview and full violationCount. View violations and GET <api>/security/rules/{id}/violations?scanId=...&offset=0&limit=100 read bounded, sanitized details retained by the same scan. Retention truncation is separate from security evidence coverage; see snapshot, retention, and MCP/CLI retrieval. QS-AUTHZ-004 preserves its aggregate preview while retaining endpoint identities when metadata provides them. Legacy count-only observations retain zero endpoint identities and explicitly report incomplete details.
This is the Quarkus replacement for the Spring ruleset in SECURITY-CHECKS.md: the panel and DTO are shared, but the framework-specific registries are mutually exclusive (Elytron/OIDC vs Spring Security). Equivalent authentication, authorization, transport, and CORS risks intentionally have framework-native rules on both stacks; Spring-only concepts (filter chains, FilterChainProxy, method-security proxies) are not evaluated here, and Quarkus-only concepts below are not evaluated on Spring.
Availability and bounds
Known-findings scores follow the same score eligibility policy as Spring. A required observation left unknown by native evaluation produces PARTIAL even when collection succeeded and no finding was emitted. Inapplicable extension checks alone do not make coverage incomplete.
The advisor is available on Quarkus without a security extension. Supported configuration facts are collected on explicit scans; endpoint declarations are captured at build time. Missing, inactive, unsupported and invalid observations are distinct: invalid or unreadable evidence is not silently treated as an absent control. Known findings survive unrelated incomplete collection. Unsupported analysis produces a partial scan; genuine failures use sanitized analysis errors.
This catalog was audited against Quarkus 3.33.3.1. Configuration metadata cannot establish arbitrary custom policies, dynamic tenants, delivered headers or network reachability. Collection never invokes tenant resolvers, credential providers, custom policies or HTTP-security initializers to resolve these gaps. Secret-hygiene classification is limited to recognized local application configuration sources; arbitrary external/dynamic sources are not read to label their values as committed secrets. Bounded metadata discovery also recognizes native SmallRye environment and system-property sources, without classifying their values as local-file secrets. For example, OIDC configured through a system property is not mistaken for an absent authentication mechanism. Native runtime-registry and test-URL bridges are not mistaken for arbitrary configuration inventories. Non-default @ApplicationPath, quarkus.rest.path, and quarkus.http.root-path prefixes make endpoint coverage incomplete rather than comparing unprefixed declarations with listener paths. After Arc initialization, the advisor compares raw security annotations with already-materialized interceptor bindings. A discrepancy, or a native additional-secured-method declaration, makes the affected endpoint's annotation evidence unknown. Unrelated transformations do not invalidate all endpoints. Binding agreement is not proof of an actual SecurityCheck or request authorization, and this is not a universal inventory of transformed routes and security checks. The native annotation stores are lazy, so even an annotation query can execute application transformation code; the advisor does not query those stores or replay transformers. Independent configuration findings are retained. A forwarding configuration (quarkus.http.proxy.proxy-address-forwarding=true) is not treated as proof that a proxy actually terminates TLS, so listener-level transport findings remain visible and explain that a verified terminating proxy can make them acceptable. A handful of rules — marked Quarkus-specific below — have no Spring Security equivalent at all: they cover Quarkus-only capabilities (gRPC, GraphQL, SmallRye Reactive Messaging). The rest address the same concerns the Spring Security advisor already checks, adapted to Quarkus's own config keys and extensions.
Severity scale
| Severity | Meaning |
|---|---|
| CRITICAL | Exposes credentials or secrets, or disables a critical control. |
| HIGH | Commonly leaves the application exposed. Usually fix before production. |
| MEDIUM | A hardening gap that warrants review. |
| LOW | Lower-impact hygiene. |
| INFO | Informational. The right fix depends on context. |
The panel lists only checks with findings, ordered by severity, count, then rule id.
The advisor score applies the shared severity penalty to every concrete finding, not just once per violated rule. Dismissed rules remove all of their findings from the score.
Filter the list, then jump to a check — the detail below narrows to match.
QS-AUTH-001HIGHNo authentication mechanism configuredQS-AUTH-002HIGHBasic authentication without TLSQS-AUTH-003LOWReview form authentication CSRF defensesQS-AUTH-004MEDIUMJWT verification without an expected issuerQS-AUTH-007MEDIUMEmbedded identity store enabled for productionQS-AUTH-008MEDIUMJWT verification without audience validationQS-AUTH-009INFOReview static JWT trust-anchor rotationQS-AUTH-010HIGHJDBC identity store using clear-text password mapperQS-AUTH-012HIGHForm authentication without TLSQS-AUTH-013HIGHEmbedded users stored with plain-text passwordsQS-AUTHZ-001HIGHNo path or role authorizationQS-AUTHZ-002INFOPermission policy permits all pathsQS-AUTHZ-004MEDIUMNo deny-by-default for unannotated endpointsQS-TLS-001LOWInsecure requests enabledQS-TLS-002INFONo TLS configured for the main HTTP listenerQS-TLS-003HIGHTLS certificate validation disabledQS-TLS-004HIGHIdentity-provider and JWK endpoints should use HTTPSQS-TLS-005HIGHTLS hostname verification disabledQS-TLS-006LOWLegacy TLS protocol versions configuredQS-CORS-001LOWCORS allows any originQS-CORS-002HIGHCORS wildcard origin with credentialsQS-HDR-001LOWWeak Strict-Transport-Security policyQS-HDR-002MEDIUMWeak Content-Security-PolicyQS-HDR-003LOWMissing Strict-Transport-Security headerQS-HDR-004LOWMissing Content-Security-Policy headerQS-HDR-005LOWMissing clickjacking protectionQS-HDR-006LOWMissing X-Content-Type-Options headerQS-DEV-001HIGHOIDC TLS verification disabledQS-DEV-002MEDIUMSwagger/GraphQL UI always includedQS-DEV-003LOWSmallRye Health UI always includedQS-OIDC-001HIGHOIDC without token audience validationQS-OIDC-002MEDIUMOIDC web-app session cookie not forced secureQS-OIDC-003MEDIUMPublic OIDC client without PKCEQS-OIDC-004HIGHOIDC token issuer validation is bypassedQS-OIDC-005MEDIUMOIDC session token encryption disabledQS-MGMT-001LOWManagement interface on a non-loopback hostQS-MGMT-003INFOManagement interface has no explicit prod-scoped host bindingQS-PROXY-001LOWForwarded headers trusted from any addressQS-CFG-001MEDIUMPossible secret in configurationQS-SESSION-001MEDIUMForm-auth session cookie not HttpOnlyQS-SESSION-002LOWForm-auth session cookie SameSite=NoneQS-SESSION-003LOWLong form-auth idle timeoutQS-GRPC-001MEDIUMgRPC server reflection enabled in the prod profileQS-GRAPHQL-001LOWGraphQL schema introspection enabledQS-MSG-001HIGHMessaging credentials configured without an encrypted protocol
Authentication
QS-AUTH-001 - No authentication mechanism configured
- Severity: HIGH
- Detects: Supported observations find no active OIDC, JWT, basic, form, or mTLS mechanism, protective permission policy, restrictive endpoint annotation, default roles, or deny-unannotated default, while at least one REST-server endpoint is declared.
@PermitAllandpolicy=permitare not restrictive controls. REST clients and unrelated secured beans are not endpoint evidence; unsupported custom authorization or incomplete endpoint metadata prevents an absence conclusion. - Recommendation: Add an auth mechanism (
quarkus-oidc,quarkus-smallrye-jwt,quarkus.http.auth.basic) or restrict endpoints with@RolesAllowed/@PermissionsAllowed/quarkus.http.auth.permission.*. - Learn more: https://quarkus.io/version/3.33/guides/security-overview
QS-AUTH-002 - Basic authentication without TLS
- Severity: HIGH
- Detects: Basic authentication is active while
quarkus.http.insecure-requests=enabledaccepts plain HTTP. Credentials submitted through that listener would lack transport encryption; the scan does not observe submitted credentials or external ingress policy. - Recommendation: Set
insecure-requests=redirect(ordisabled) and configure SSL. - Learn more: https://quarkus.io/version/3.33/guides/security-basic-authentication
QS-AUTH-003 - Review form authentication CSRF defenses
- Severity: LOW
- Detects:
quarkus.http.auth.form.enabled=true(cookie-based login) without theio.quarkus:quarkus-rest-csrfextension, or withquarkus.rest-csrf.enabled=falseorquarkus.rest-csrf.verify-token=false. This is a review of the standard defense, not proof that custom CSRF defenses are absent. Extension presence alone does not establish verification or coverage of every path, method, or media type. - Recommendation: Verify coverage of state-changing browser requests; use
quarkus-rest-csrfand embed its token in forms where appropriate. - Learn more: https://quarkus.io/version/3.33/guides/security-csrf-prevention
QS-AUTH-004 - JWT verification without an expected issuer
- Severity: MEDIUM
- Detects: The JWT capability and a supported verification key source are active, but
mp.jwt.verify.issueris absent. Supported sources include the inline MicroProfile key,mp.jwt.verify.publickey.location, and the overridingsmallrye.jwt.verify.key.location. Custom verifiers remain unknown; no verifier is executed to establish acceptance. - Recommendation: Set
mp.jwt.verify.issuerto the expected token issuer. - Learn more: https://quarkus.io/version/3.33/guides/security-jwt
Retired: QS-AUTH-005. Proactive authentication controls when credentials are processed, not whether authorization is enforced. The supported deferred mode is not an independent security deficiency. Authorization coverage is reviewed by the applicable authorization rules; this ID remains reserved.
Retired: QS-AUTH-006 (JWT signature algorithm not pinned for a remote JWKS) was removed. MicroProfile JWT 2.1 defines
mp.jwt.verify.publickey.algorithmwith anRS256default and explicitly describes the property as the algorithm whitelist. Leaving it unset therefore does not accept an unbounded algorithm set; the old rule reported a missing explicit preference even though the effective verifier remained pinned. The rule id is retired and will not be reused.
QS-AUTH-007 - Embedded identity store enabled for production
- Severity: MEDIUM
- Detects: The properties identity-store capability is present and supported local prod declarations enable
quarkus.security.users.embedded.enabled(%prod.first, then the base key). BootUI runs in dev/test, so a%dev/%test-only declaration — the configuration this rule recommends — is not a finding, while an explicit%prod=falseoverrides basetrue. The embedded store is distinct from the file store. This reviews configuration, not a running production deployment; environment and external sources are not read for this review. - Recommendation: Review the identity-store choice for each deployment; keep demonstration users local and use an appropriate production identity provider or password-hashing store.
- Learn more: https://quarkus.io/version/3.33/guides/security-properties#embedded-users
QS-AUTH-008 - JWT verification without audience validation
- Severity: MEDIUM
- Detects: The JWT capability and a supported verification key source are active, but no
mp.jwt.verify.audiencesdeclaration is observed. Scalar and supported indexed lists are recognized. This reviews the expected audience configuration rather than executing tokens or inferring custom validation from unrelated beans. - Recommendation: Set
mp.jwt.verify.audiencesto this service's expected audience(s). - Learn more: https://quarkus.io/version/3.33/guides/security-jwt
QS-AUTH-009 - Review static JWT trust-anchor rotation
- Severity: INFO
- Detects: Supported JWT configuration selects an inline
mp.jwt.verify.publickeywithout an overriding key location. - Why it matters: Static trust anchors are supported and can rotate out of band. This is an operational reminder, not an inherent weakness or a requirement to use remote JWKS.
- Recommendation: Document and test the trust-anchor replacement process.
- Learn more: https://quarkus.io/version/3.33/guides/security-jwt
QS-AUTH-010 - JDBC identity store using clear-text password mapper
- Severity: HIGH
- Detects: The JDBC security capability and
quarkus.security.jdbc.enabled=trueare active, and a supportedquarkus.security.jdbc.principal-query[.<name>].clear-password-mapper.enabled=truedeclaration selects clear-text password comparison. Unrelated keys containingprincipal-query, disabled stores, and unsupported named-query syntax do not establish this finding. - Recommendation: Switch to
bcrypt-password-mapper(or another hashing mapper) and re-hash stored passwords. - Learn more: https://quarkus.io/version/3.33/guides/security-jdbc
QS-AUTH-012 - Form authentication without TLS
- Severity: HIGH
- Detects:
quarkus.http.auth.form.enabled=truewhilequarkus.http.insecure-requests=enabledaccepts passwords over plain HTTP, exposing them to passive network observers and active intermediaries. Forwarded-header trust does not suppress the finding because it does not prove that a proxy terminates TLS. - Recommendation: Set
quarkus.http.insecure-requests=redirect(ordisabled) and configure TLS. - Learn more: https://quarkus.io/version/3.33/guides/security-authentication-mechanisms#form-auth
QS-AUTH-013 - Embedded users stored with plain-text passwords
- Severity: HIGH
- Detects: Supported local prod declarations set both
quarkus.security.users.embedded.enabled=trueandquarkus.security.users.embedded.plain-text=true, making the embedded identity store accept literal passwords. Quarkus defaultsplain-texttofalseand otherwise expects a digest derived fromusername:realm:password. Like QS-AUTH-007,%dev/%test-only demo users are not production evidence. - Recommendation: Use a production identity provider or a supported adaptive password-hashing store. The embedded store's legacy digest default is not a recommendation for modern production password storage.
- Learn more: https://quarkus.io/version/3.33/guides/security-properties#embedded-users
Retired: QS-AUTH-011 (JDBC identity store bcrypt work-factor too low) was removed. The rule checked
principal-query.*.bcrypt-password-mapper.work-factor, a property that does not exist:BcryptPasswordKeyMapperConfig(quarkus-elytron-security-jdbc) has no work-factor/cost-factor field at all (onlyenabled,password-index,hash-encoding,salt-index,salt-encoding,iteration-count-index— a column index, not a cost factor). Bcrypt's cost factor is embedded in the stored MCF-format hash string itself, not externally configurable via this extension, so the rule could never fire and its remediation ("raise the work factor") was nonsensical. The rule id is retired and will not be reused.
Authorization
QS-AUTHZ-001 - No path or role authorization
- Severity: HIGH
- Detects: An active auth mechanism and declared REST-server endpoints are observed, but no supported restrictive permission policy, endpoint annotation, default roles, or deny-unannotated default is found. Disabled policies and
policy=permitdo not count as protection. Unsupported custom/global policies or endpoint metadata remain unknown. - Recommendation: Add
@RolesAllowed/@PermissionsAllowed/@Authenticatedor path permissions withpolicy=authenticated/roles. - Learn more: https://quarkus.io/version/3.33/guides/security-authorize-web-endpoints-reference
QS-AUTHZ-002 - Permission policy permits all paths
- Severity: INFO
- Detects: A non-shared,
applies-to=allpermission declarespolicy=permitfor/*without a method restriction. Supported scope names are case-normalized, including the nativeALLdefault; unsupported values remain unknown./is an exact path, not an application-wide wildcard. This is a public-default declaration, not proof that authentication is disabled: more-specific mappings, same-path restrictions, shared policies, endpoint annotations, and REST defaults can still restrict requests. - Recommendation: Confirm the public default is intentional; narrow it or select a restrictive policy where needed.
- Learn more: https://quarkus.io/version/3.33/guides/security-authorize-web-endpoints-reference
Retired: QS-AUTHZ-003. An arbitrary annotation ratio is not effective authorization coverage. Path policies, defaults, public declarations and custom policies cannot be reduced to a percentage of annotated methods. The ID remains reserved.
QS-AUTHZ-004 - No deny-by-default for unannotated endpoints
- Severity: MEDIUM
- Detects: Authentication is active, deny-unannotated and default roles are absent in the current runtime, and a directly declared unannotated REST endpoint has no supported matching restriction. The bounded analysis distinguishes exact
/from/*, longest-path and exact-path precedence, method-specific mappings, same-path restrictions, and shared policies. HTTP (all) and REST (jaxrs) scopes choose their matching policies independently, then intersect restrictions: a more-specific permit in one phase cannot override denial in the other. A matched path with no matching method mapping denies that method; a GET-only mapping does not establish that POST is public. Method annotations override class annotations, and explicit@PermitAlldeclarations are not counted as accidentally unannotated. Custom policies, unresolved route templates, inherited/transformed metadata, non-default listener prefixes, and unsupported scopes remain unknown rather than being executed or treated as public. - Recommendation: Set
deny-unannotated-endpoints=true(or default roles) and mark public endpoints@PermitAll. - Learn more: https://quarkus.io/version/3.33/guides/security-authorize-web-endpoints-reference
Transport
QS-TLS-001 - Insecure requests enabled
- Severity: LOW
- Detects:
quarkus.http.insecure-requests=enabledserves plain HTTP. The rule uses Quarkus's effective default: absent meansdisabledwhenquarkus.http.ssl.client-auth=required, andenabledotherwise. Forwarding configuration does not suppress it. - Why it matters: Acceptable in local dev or behind a TLS-terminating proxy; risky if exposed directly.
- Recommendation: Prefer
redirectonce TLS is available, or document the terminating proxy. - Learn more: https://quarkus.io/version/3.33/guides/http-reference#ssl
QS-TLS-002 - No TLS configured for the main HTTP listener
- Severity: INFO
- Detects: No supported main-listener TLS material is declared. Recognized forms include a legacy keystore, paired certificate/key lists, TLS-registry JKS/P12 paths, and paired PEM certificate/key entries. A named registry bucket only counts when selected by
quarkus.http.tls-configuration-name; an unrelated client bucket does not. Partial material and programmatic providers remain incomplete, and a declaration is not proof of usable HTTPS. Password, alias, ordering, and other option defaults are not certificate material and do not, by themselves, make TLS observation incomplete. - Recommendation: Acceptable behind a verified terminating proxy.
- Learn more: https://quarkus.io/version/3.33/guides/http-reference#ssl
QS-TLS-003 - TLS certificate validation disabled
- Severity: HIGH
- Detects:
trust-all=trueis set on the default TLS registry bucket (quarkus.tls.trust-all) or any named bucket (quarkus.tls.<name>.trust-all), disabling peer certificate validation wherever that bucket is used and creating a transport-validation risk. The scan does not establish whether every named bucket is consumed. - Recommendation: Remove
trust-all; import the peer's CA into a trust-store instead. - Learn more: https://quarkus.io/version/3.33/guides/tls-registry-reference#trusting-all-certificates-and-hostname-verification
QS-TLS-004 - Identity-provider and JWK endpoints should use HTTPS
- Severity: HIGH
- Detects: An active OIDC tenant's auth-server URL or an active JWT verifier's effective remote key location uses plain HTTP.
smallrye.jwt.verify.key.locationoverridesmp.jwt.verify.publickey.location; stale configuration for absent capabilities or disabled tenants does not establish a finding. No discovery, key retrieval, or other network request is made, and endpoint values are not retained in report samples. - Recommendation: Use HTTPS endpoints with certificate validation enabled.
- Learn more: https://quarkus.io/version/3.33/guides/security-oidc-bearer-token-authentication#bearer-token-jwt-claim-verification
QS-TLS-005 - TLS hostname verification disabled
- Severity: HIGH
- Detects:
quarkus.tls.hostname-verification-algorithm=NONE, the equivalent setting on a named TLS registry bucket, or legacy OIDCtls.verification=certificate-validationvalidates certificate chains without checking that the certificate belongs to the requested host. A named OIDC TLS configuration supersedes the legacy OIDC setting and avoids a duplicate/obsolete finding. - Recommendation: Enable hostname verification for each applicable consumer. TLS-registry defaults depend on the consumer; the presence of a bucket alone does not establish a connection using it.
- Learn more: https://quarkus.io/version/3.33/guides/tls-registry-reference#trusting-all-certificates-and-hostname-verification
QS-TLS-006 - Legacy TLS protocol versions configured
- Severity: LOW
- Detects: An effective
quarkus.http.ssl.protocols,quarkus.tls.protocols, orquarkus.tls.<name>.protocolslist (scalar or indexed) enablesTLSv1,TLSv1.1, orSSLv3, compared case-insensitively.SSLv2Hellois a ClientHello compatibility format, not a protocol version, and is not flagged. Like Quarkus'sHttpServerOptionsUtils, the legacy HTTP list is ignored whenquarkus.http.tls-configuration-nameor a default registry key store owns the listener. Report samples are value-free declaration labels; unresolved lists remain incomplete. - Why it matters: RFC 8996 deprecates TLS 1.0 and 1.1. The JDK disables them by default through
jdk.tls.disabledAlgorithms, so an explicit opt-in is often inert, and the scan does not establish that a named bucket is consumed or that a listener or client negotiates a legacy version. - Recommendation: Remove the legacy versions. Quarkus defaults the HTTP list to
TLSv1.3,TLSv1.2and the TLS registry toTLSv1.3; isolate a legacy peer in a dedicated named TLS configuration. - Learn more: https://quarkus.io/version/3.33/guides/tls-registry-reference#tls-protocol-versions
CORS
QS-CORS-001 - CORS allows any origin
- Severity: LOW
- Detects: Enabled CORS permits universal noncredentialed response sharing. A literal
*is universal only as the sole origin; recognized universal regexes (/.*/or/^.*$/) also match universally inside a list.*,https://app.exampleis not a universal literal wildcard, while/.*/,https://app.exampleis universal. Empty origins remain restrictive. Explicitquarkus.http.cors.enabled=falsewins over the legacy enable setting. Arbitrary regexes are not executed or classified as safe. - Recommendation: Confirm public response sharing is intentional, or configure trusted origins. CORS is not an authentication or authorization boundary.
- Learn more: https://quarkus.io/version/3.33/guides/security-cors
QS-CORS-002 - CORS wildcard origin with credentials
- Severity: HIGH
- Detects: A supported universal origin configuration allows credentials. Explicit
quarkus.http.cors.access-control-allow-credentialswins; otherwise both exact and regex origin matches default credentials totrue. A sole*or sole/.*/is Quarkus's wildcard origin (CORSFilter.isOriginConfiguredWithWildcard), which defaults credentials tofalseand is reviewed by QS-CORS-001 instead. A universal regex inside a list, or a sole/^.*$/, is matched as a regex and defaults credentials totrue. Quarkus reflects the request Origin, so this is not the browser-rejected literalAccess-Control-Allow-Origin: *plus credentials combination. QS-CORS-001 is not also emitted for the same credentialed policy. - Recommendation: Pin explicit origins; never combine wildcard with credentials.
- Learn more: https://quarkus.io/version/3.33/guides/security-cors
Retired: QS-CORS-003. Reflecting requested methods/headers for an allowed origin is supported Quarkus/Fetch behavior, not an independent bypass of the origin trust boundary. Least-privilege API design can still use narrower lists. The ID remains reserved.
Retired: QS-CORS-005. Unset origins are restrictive, not a security gap. The filter is not inert: it rejects disallowed cross-origin requests. The ID remains reserved.
Retired: QS-CORS-004 (CORS regex origin pattern not anchored) was removed. The rule claimed Quarkus's
CORSFiltermatched an unanchored/regex/origin pattern anywhere in the string (.find()semantics) rather than against the whole string, citing quarkusio/quarkus#34718. Direct inspection of the currentCORSFilter.isOriginAllowedByRegexshowspattern.matcher(origin).matches()— Java's.matches()requires a full match of the entire input string, not.find()— and issue #34718 was fixed in Quarkus 3.3.0, long before this project's current Quarkus line. The bypass the rule warned about no longer applies to any Quarkus version this project supports, so the rule was removed rather than re-worded; preferring literal origins over regex (and anchoring any regex you do use) remains sound general advice, just not something this advisor asserts a specific exploitable mechanism for. The rule id is retired and will not be reused.
Headers
QS-HDR-001 - Weak Strict-Transport-Security policy
- Severity: LOW
- Detects: A supported global HSTS declaration has an invalid lifetime, disables HSTS with
max-age=0, or uses a lifetime shorter than one year. Zero disables the policy; a short nonzero lifetime may be an intentional rollout. Multiple or scoped declarations are not combined into an effective delivered policy. - Recommendation: Review the lifetime and rollout plan. One year is a review baseline, not a protocol minimum.
includeSubDomainsis optional and should be enabled only when every subdomain is HTTPS-ready. - Learn more: https://quarkus.io/version/3.33/guides/http-reference#additional-http-headers
QS-HDR-002 - Weak Content-Security-Policy
- Severity: MEDIUM
- Detects: One supported global enforcing CSP permits unsafe inline scripts, unsafe evaluation, or an unrestricted script source. The bounded parser observes
script-src/default-srcfallback,script-src-elemandscript-src-attr, first-duplicate-directive semantics, nonce/hash/strict-dynamicexceptions, and restrictive script overrides. Style-only inline permissions and scoped host wildcards are not arbitrary-script-source proof. Report-only policy does not establish enforcement; enforcing plus report-only is valid. Multiple policies, unsupported syntax, unknown custom-writer ordering, and uncertain path/method scope remain incomplete. - Recommendation: Remove unsafe-inline/unsafe-eval and wildcard sources; use nonces/hashes for scripts.
- Learn more: https://quarkus.io/version/3.33/guides/http-reference#additional-http-headers
QS-HDR-003 - Missing Strict-Transport-Security header
- Severity: LOW
- Detects: Declared document endpoints and supported listener TLS configuration are present, but no global HSTS declaration is observed. This is configuration review, not proof of missing delivered headers. Custom filters, proxies, and uncertain header scope prevent an absence conclusion.
- Recommendation: Add
quarkus.http.header."Strict-Transport-Security".value=max-age=31536000; addincludeSubDomainsonly after every subdomain is HTTPS-ready. - Learn more: https://quarkus.io/version/3.33/guides/http-reference#additional-http-headers
QS-HDR-004 - Missing Content-Security-Policy header
- Severity: LOW
- Detects: Declared document endpoints have no observed global enforcing CSP declaration. A report-only policy alone is not enforcing, but an additional report-only policy does not invalidate an enforcing one. API-only or uncertain document applicability, custom filters, and scoped/multiple header declarations remain incomplete.
- Recommendation: Add a CSP tailored to the app's script/style/asset origins.
- Learn more: https://quarkus.io/version/3.33/guides/http-reference#additional-http-headers
QS-HDR-005 - Missing clickjacking protection
- Severity: LOW
- Detects: Declared document endpoints have no supported restrictive framing declaration after applying precedence. An enforcing CSP
frame-ancestorsdirective overrides X-Frame-Options even when permissive (*); an empty ancestor list blocks all framing. Valid globalX-Frame-Options: DENY/SAMEORIGINis an alternative only when the enforcing ancestor directive is known absent. Invalid XFO values do not establish protection. Report-only directives do not override XFO; unknown CSP composition or writer/path/method scope cannot be assumed safe because XFO exists. - Recommendation: Use a restrictive enforcing CSP
frame-ancestors, such as'none'. XFODENYis an alternative only without an overriding enforcing ancestor directive. - Learn more: https://quarkus.io/version/3.33/guides/http-reference#additional-http-headers
QS-HDR-006 - Missing X-Content-Type-Options header
- Severity: LOW
- Detects: No valid global
X-Content-Type-Options=nosniffdeclaration is observed. Header names and the recognized value are compared case-insensitively; an arbitrary nonblank value does not count. Scoped headers and custom response filters remain unknown, and the scan does not observe proxy-delivered or runtime-written headers. - Recommendation: Add
quarkus.http.header."X-Content-Type-Options".value=nosniff. - Learn more: https://quarkus.io/version/3.33/guides/http-reference#additional-http-headers
Retired: QS-HDR-007. Modern browsers default to
strict-origin-when-cross-origin; the absence of an explicit Referrer-Policy does not establish the full-URL disclosure asserted by the former rule. Applications can still select an explicit policy for their needs. The ID remains reserved.
Retired: QS-HDR-008. Missing Permissions-Policy alone does not establish a meaningful defect without application feature and embedding context. Feature-specific browser policy remains a design choice. The ID remains reserved.
Dev exposure
QS-DEV-001 - OIDC TLS verification disabled
- Severity: HIGH
- Detects:
quarkus.oidc.tls.verification=nonedisables provider certificate validation. This legacy setting is deprecated in Quarkus 3.33 in favor of a TLS registry configuration. Only active tenants are considered; a selected named TLS configuration overrides the legacy setting. - Recommendation: Sometimes used against a local dev provider, but must never reach production.
- Learn more: https://quarkus.io/version/3.33/guides/tls-registry-reference#trusting-all-certificates-and-hostname-verification
QS-DEV-002 - Swagger/GraphQL UI always included
- Severity: MEDIUM
- Detects: The corresponding capability is present and supported local prod declarations enable
quarkus.swagger-ui.always-includeorquarkus.smallrye-graphql.ui.always-include. An explicit%prod=falseoverrides basetrue; a dev-only declaration is not production evidence. Inclusion does not prove an enabled route, anonymous access, or a running production deployment.quarkus.smallrye-openapi.always-includedoes not exist and is not evaluated. - Recommendation: Restrict it to dev, or remove
always-include. - Learn more: https://quarkus.io/version/3.33/guides/openapi-swaggerui#swagger-ui
QS-DEV-003 - SmallRye Health UI always included
- Severity: LOW
- Detects: The Health capability is present and supported local prod declarations enable
quarkus.smallrye-health.ui.always-include, honoring explicit prod overrides. Inclusion is distinct from route availability, access policy, and production exposure; none is inferred from inclusion alone. - Recommendation: Remove the override so the Health UI is only available outside production, or protect it via the management interface / a permission policy.
- Learn more: https://quarkus.io/version/3.33/guides/smallrye-health#ui
OIDC
QS-OIDC-001 - OIDC without token audience validation
- Severity: HIGH
- Detects: OIDC is configured for a default or named
service/hybridtoken-consuming tenant without that tenant's expectedquarkus.oidc[.<tenant>].token.audience, or with the exact singletonany, which disables audience validation. Scalar and supported indexed lists are recognized;any,serviceis not the singleton sentinel. Absent capabilities, disabled tenants, and unknown custom validation are not missing-audience proof. Pureweb-appauthorization-code clients are excluded because their primary authentication artifact is an OIDC ID token whose audience is validated against the client id by the protocol implementation; applying this resource-server rule to them would be a false positive. - Why it matters: The severity is HIGH for service/M2M flows because RFC 8725 requires each JWT application to validate that the token was issued for it.
- Recommendation: Set the tenant's token audience to this resource server's expected audience.
- Learn more: https://quarkus.io/version/3.33/guides/security-oidc-bearer-token-authentication#bearer-token-jwt-claim-verification
QS-OIDC-002 - OIDC web-app session cookie not forced secure
- Severity: MEDIUM
- Detects: An active OIDC
web-app/hybridtenant does not force cookie Secure while the listener accepts HTTP. Without the override, Secure is request-dependent; HTTPS key material alongside an accepted HTTP listener does not make HTTP session cookies secure. Disabled or redirected HTTP does not trigger this finding. - Recommendation: Disable or redirect HTTP and review
quarkus.oidc.authentication.cookie-force-secure, including trusted proxy handling. The scan does not establish external TLS termination. - Learn more: https://quarkus.io/version/3.33/guides/security-oidc-code-flow-authentication#oidc-cookies
QS-OIDC-003 - Public OIDC client without PKCE
- Severity: MEDIUM
- Detects: Supported client-authentication metadata establishes a public
web-app/hybridclient without PKCE. Missing literalcredentials.secretalone is insufficient: client-secret providers, JWT client authentication, supported key/secret-provider settings, and provider presets are considered. Spotify and Twitter/X presets enable PKCE by default; an explicitauthentication.pkce-required=falseoverrides that default. Dynamic tenants, custom providers, unsupported source/profile combinations, and incomplete credential metadata remain unknown. - Recommendation: Set
quarkus.oidc.authentication.pkce-required=truefor public clients. - Learn more: https://quarkus.io/version/3.33/guides/security-oidc-code-flow-authentication#proof-key-for-code-exchange-pkce
QS-OIDC-004 - OIDC token issuer validation is bypassed
- Severity: HIGH
- Detects: The exact lowercase
quarkus.oidc[.<tenant>].token.issuer=anydisables issuer matching for an active default or named tenant.ANYis not that sentinel. Supported provider defaults also count: the Microsoft preset suppliesanyunless explicitly overridden. Disabled tenants are excluded, and no token or tenant resolver is invoked. - Recommendation: Remove
token.issuer=anyand pin the exact trusted issuer; use explicit tenant resolution when multiple issuers are intentional. - Learn more: https://quarkus.io/version/3.33/guides/security-oidc-bearer-token-authentication#bearer-token-jwt-claim-verification
QS-OIDC-005 - OIDC session token encryption disabled
- Severity: MEDIUM
- Detects: An active default or named
web-app/hybridtenant setsquarkus.oidc[.<tenant>].token-state-manager.encryption-required=false(Quarkus defaulttrue). Service tenants and disabled tenants are excluded; dynamic tenants are not evaluated. - Why it matters: The default token state manager then stores the retained ID, access, and refresh tokens in the session cookie without encryption. When encryption is required, Quarkus also encrypts tokens before handing them to a custom (database or Redis) token state manager; disabling it hands them over in plain text. Independent encryption by a custom manager is not observed. The cookie stays HttpOnly by default, so this is defense in depth for bearer artifacts, not a replacement for cookie protection.
- Recommendation: Remove the override so
token-state-manager.encryption-requiredkeeps itstruedefault. - Learn more: https://quarkus.io/version/3.33/guides/security-oidc-code-flow-authentication#token-state-manager
Management
QS-MGMT-001 - Management interface on a non-loopback host
- Severity: LOW
- Detects: The separate management interface is enabled in the observed runtime and its resolved
quarkus.management.hostis non-loopback. Binding is not proof of remote reachability or anonymous access. Current runtime evidence is separate from the bounded prod declarations reviewed by QS-MGMT-003. Launch mode must not be inferred from a profile name:RUNandNORMALcan both default toprod, while BootUI remains excluded fromNORMAL. - Recommendation: Bind the host to
127.0.0.1, or protect the management endpoints. - Learn more: https://quarkus.io/version/3.33/guides/management-interface-reference#configure-the-host-port-and-scheme
Retired: QS-MGMT-002. Sharing an application namespace does not itself create endpoints, remove authorization or prove wider exposure. Paths and protection need endpoint-specific evidence. The ID remains reserved.
QS-MGMT-003 - Management interface has no explicit prod-scoped host binding
- Severity: INFO
- Detects: Supported local prod declarations enable management without either a base
quarkus.management.hostor%prod.quarkus.management.hostdeclaration. Explicit prodenabled=falsewins over basetrue; an inactive production declaration does not trigger the rule. Unresolved or unsupported source/profile evidence remains incomplete. - Why it matters: The prod-profile default is all interfaces, unlike the dev/test loopback default. This reviews a possible deployment configuration, not an observed production listener. It can differ from the runtime state reviewed by QS-MGMT-001 and does not establish launch mode, firewall policy, or endpoint authorization.
- Recommendation: Explicitly pin
%prod.quarkus.management.hostto127.0.0.1, or to the intended bind address. - Learn more: https://quarkus.io/version/3.33/guides/management-interface-reference#configure-the-host-port-and-scheme
Proxy
QS-PROXY-001 - Forwarded headers trusted from any address
- Severity: LOW
- Detects: Supported local prod declarations (
%prod.first, then the base key) enablequarkus.http.proxy.proxy-address-forwardingwith forwarded-header processing —allow-forwarded=true, orallow-x-forwarded, which defaults to!allow-forwardedexactly as in Quarkus'sForwardingProxyOptions— andquarkus.http.proxy.trusted-proxiesis absent, empty, or contains a universal range (0.0.0.0/0,::/0). An effective runtimetrusted-proxiesvalue (for example from the environment) also counts as a restriction. Unresolved declarations remain incomplete. - Why it matters: Without trusted proxies Quarkus accepts
Forwarded/X-Forwarded-*from every peer, so a client that reaches the listener directly, or through a proxy that does not strip those headers, can spoof the client address and scheme (and the host whenenable-forwarded-hostis set). The Quarkus HTTP reference warns that activating forwarding "leaves the server exposed to several security issues (i.e. information spoofing)". Network isolation and proxy header stripping are not observable, hence LOW. - Recommendation: Set
quarkus.http.proxy.trusted-proxiesto the proxy addresses or CIDR ranges, and make the proxy strip client-supplied forwarded headers. - Learn more: https://quarkus.io/version/3.33/guides/http-reference#reverse-proxy
Config hygiene
QS-CFG-001 - Possible secret in configuration
- Severity: MEDIUM
- Detects: A config key's terminal segment identifies a password, secret, API key, private key, token, access/refresh token, or symmetric key (
encryption-key,signature-key,signing-key,secretkey) set to a literal value (not an externalized${...}reference). Inline private keys whose terminal segment is the generickeyare matched by exact name only:smallrye.jwt.sign.key,smallrye.jwt.decrypt.key, andquarkus.oidc[.<tenant>].credentials.jwt.key. Public keys (mp.jwt.verify.publickey) and*.location/*-filereferences are excluded. Scans application and%prodconfiguration, including thequarkus.*namespace — e.g.quarkus.datasource.password,quarkus.oidc.credentials.secret,quarkus.mail.passwordare all in scope, alongside application-owned keys. A committedquarkus.http.auth.session.encryption-key(form-auth cookie encryption),quarkus.rest-csrf.token-signature-key(CSRF token HMAC) orsmallrye.jwt.verify.secretkey(HMAC JWT verification) lets anyone with the source forge sessions, CSRF tokens or JWTs. - Scope: Only recognized local application properties/YAML source provenance is inspected for literal credentials. Environment-variable, system-property, config-tree, remote, and custom sources are not enumerated or read to infer committed secrets.
${...}expressions,%dev/%testvalues, BootUI internals, and metadata keys such asquarkus.oidc.token.issuerare excluded. Bounded safe labels enter report samples; values and application exception text never do. This is local source hygiene, not proof of credential exposure, current use, or production deployment. - Recommendation: Move committed literals to a vault/env var.
- Learn more: https://quarkus.io/version/3.33/guides/credentials-provider
Session
QS-SESSION-001 - Form-auth session cookie not HttpOnly
- Severity: MEDIUM
- Detects:
quarkus.http.auth.form.http-only-cookiedefaults tofalsein Quarkus 3.33 (FormAuthConfig) — unlike most frameworks — so the form-auth session cookie is readable from JavaScript; any XSS flaw can then steal the session. Exploitation needs a separate XSS flaw, so the rule is MEDIUM, matching the SpringSEC-SESSION-003control. - Recommendation: Set
quarkus.http.auth.form.http-only-cookie=true. - Learn more: https://quarkus.io/version/3.33/guides/security-authentication-mechanisms#form-auth
QS-SESSION-002 - Form-auth session cookie SameSite=None
- Severity: LOW
- Detects: Active form authentication selects
quarkus.http.auth.form.cookie-same-site=none, allowing cross-site cookie use. This can be an intentional compatibility choice, not an independent proof of missing CSRF defenses. - Recommendation: Use Secure with SameSite=None and verify independent CSRF defenses. Prefer Strict/Lax when compatible with the required browser flows.
- Learn more: https://quarkus.io/version/3.33/guides/security-authentication-mechanisms#form-auth
QS-SESSION-003 - Long form-auth idle timeout
- Severity: LOW
- Detects: Active form authentication has a supported parsed
quarkus.http.auth.form.timeoutof at least eight hours. This is an idle timeout, not an absolute session lifespan: active sessions can renew. Eight hours is a heuristic review threshold; invalid or unsupported durations never become a passing default. - Recommendation: Lower the timeout (the Quarkus default is 30 minutes) and pair it with
new-cookie-interval. - Learn more: https://quarkus.io/version/3.33/guides/security-authentication-mechanisms#form-auth
gRPC
QS-GRPC-001 - gRPC server reflection enabled in the prod profile
- Severity: MEDIUM
- Detects: Quarkus-specific. The gRPC capability and a declared server service are present, and supported local prod declarations enable
quarkus.grpc.server.enable-reflection-service. Explicit%prod=falseoverrides basetrue; dev-only settings and client-only capability do not establish this finding. - Why it matters: Reflection makes service/schema metadata discoverable to permitted callers. This is a production configuration review, not proof of public access or a running production server.
- Recommendation: Remove the
%prodoverride; keep reflection enabled only in%dev/%test. - Learn more: https://quarkus.io/version/3.33/guides/grpc-service-implementation#reflection-service
GraphQL
QS-GRAPHQL-001 - GraphQL schema introspection enabled
- Severity: LOW
- Detects: Quarkus-specific. The GraphQL capability is present and supported local prod declarations leave
quarkus.smallrye-graphql.field-visibilitywithoutno-introspection, honoring the prod override before the base declaration. This reviews schema-disclosure configuration, not the access policy of a running deployment.quarkus.smallrye-graphql.introspection-enableddoes not exist and is not evaluated. - Why it matters: Often intentional for public APIs, but worth a deliberate decision.
- Recommendation: Add
no-introspectiontoquarkus.smallrye-graphql.field-visibilityin%produnless the schema is meant to be publicly discoverable. - Learn more: https://quarkus.io/version/3.33/guides/smallrye-graphql
Messaging
QS-MSG-001 - Messaging credentials configured without an encrypted protocol
- Severity: HIGH
- Detects: Quarkus-specific. An enabled channel explicitly using the Kafka connector has declared SASL password or JAAS credentials with effective
PLAINTEXTorSASL_PLAINTEXT. Protocol resolution follows channel, connector (mp.messaging.connector.smallrye-kafka), then global (kafka) settings. Global credentials can apply to a channel with an insecure override; a global bucket is not independently treated as a running channel. Disabled/non-Kafka channels are excluded, and implicit connectors or custom CDI configuration maps remain unknown without invoking producers. Report samples use bounded, value-free channel labels. - Why it matters:
SASL_PLAINTEXTlacks transport encryption for the selected authentication exchange.PLAINTEXTdoes not send SASL credentials; alongside declared credentials it indicates an inconsistent, unencrypted setup. The rule does not claim that every SASL mechanism transmits a raw password. - Recommendation: Set
security.protocol=SASL_SSL(orSSL) for each affected channel (or globally viakafka.security.protocol). - Learn more: https://quarkus.io/version/3.33/guides/kafka#tls-configuration
Audit sources and limits
The implementation review is pinned to 3.33.3.1:
Permission mapping configuration and path matching policy establish exact paths, longest match, method mismatch denial and shared policy behavior.
CORSFilter establishes reflected origins, regex matching and the conditional credentials default.
OidcProvider, tenant configuration and known provider presets define validation sentinels and provider-specific defaults.
Client credential configuration supports providers and JWT client authentication; missing a literal secret does not establish a public client.
Header configuration and Kafka configuration reference define scope and inheritance that raw property-presence checks cannot replace.
LaunchMode distinguishes
RUNfromNORMAL, although both default to theprodprofile. Profile names are not proof of launch mode or network exposure; BootUI's existingNORMALexclusion remains unchanged.Fetch, CSP3, Referrer Policy, OWASP headers, JWT BCP and OAuth BCP provide the browser/token context.
FormAuthConfig confirms the
http-only-cookie=falseandcookie-same-site=strictdefaults; ProxyConfig and ForwardingProxyOptions establish that an absenttrusted-proxieslist trusts every peer (3.33 has notrusted-proxy[n].subject-dn); ServerSslConfig and HttpServerOptionsUtils define the protocol defaults and registry precedence.Learn-more links point to the version-pinned Quarkus 3.33 guides, whose anchors were verified; the unversioned guides already document later releases.
Considered and not added: a SmallRye JWT smallrye.jwt.verify.relax-key-validation rule. Its documented default is false, but SmallRye JWT 4.6.3 (the version Quarkus 3.33 ships) declares defaultValue = "true" in JWTAuthContextInfoProvider, so the relaxation is the library default for every JWT application and is inert unless an issuer actually signs with a short RSA key, which configuration cannot observe.
Custom policies, dynamic tenants, credentials providers, external sources and delivered headers are not evaluated by executing application code. Unsupported evidence remains incomplete; no active testing or endpoint mutation is performed. Retired IDs are permanently reserved.