security: OpenAPI spec (/api/openapi.json) declares all 689 endpoints as unauthenticated due to missing securitySchemes
@mbiuki is already working on this.
Since Mar 16, 2026.
- Dominant language
- Java
- Stars
- 970
- Forks
- 486
- Avg merge
- 3d 33m
- Merged PRs (30d)
- 170
Description
Summary
The OpenAPI specification served at /api/openapi.json declares "security": [] globally with no securitySchemes defined in components. This documents all 689 endpoints as requiring no authentication, even though the server code enforces auth on the vast majority of them.
Steps to Reproduce
curl https://demo.dotcms.com/api/openapi.json | jq '.security, (.components.securitySchemes // "MISSING")'- Observe: global
securityis[],securitySchemesis absent - Run any automated security scanner (OWASP ZAP, 42Crunch) against the spec — all endpoints flagged as unauthenticated
Impact
- Attacker reconnaissance: The full 689-endpoint attack surface appears open — reducing enumeration effort to zero
- Developer misuse: Consumers of the spec may omit auth headers assuming endpoints are intentionally public
- Security audit failures: Automated scanners generate false positives on every endpoint, burying real issues
- Spec ≠ reality divergence: Erodes trust in API documentation as a security contract
Code Verification
All of the following resources properly enforce rejectWhenNoUser(true) in WebResource.InitBuilder — but the spec doesn't reflect this:
| Resource | Code Enforcement | In Spec |
|---|---|---|
MaintenanceResource |
rejectWhenNoUser(true) + admin + MAINTENANCE portlet |
No auth declared |
RedisResource |
rejectWhenNoUser(true) + MAINTENANCE portlet |
No auth declared |
SystemTableResource |
rejectWhenNoUser(true) + CMS_ADMINISTRATOR_ROLE |
No auth declared |
ApiTokenResource |
rejectWhenNoUser(true) + users portlet |
No auth declared |
UserResource |
rejectWhenNoUser(true) |
No auth declared |
LicenseResource |
rejectWhenNoUser(true) + CONFIGURATION portlet |
No auth declared |
OSGIResource |
rejectWhenNoUser(true) + DYNAMIC_PLUGINS portlet |
No auth declared |
LoggerResource |
rejectWhenNoUser(true) + isAdmin() |
No auth declared |
AppsResource |
rejectWhenNoUser(true) |
No auth declared |
PermissionResource |
rejectWhenNoUser(true) + admin checks |
No auth declared |
Root Cause
The openapi.json generator does not emit a securitySchemes block and JAX-RS resource methods lack @SecurityRequirement annotations.
Proposed Fix
- Add
securitySchemestocomponents:
"securitySchemes": {
"Bearer": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" },
"BasicAuth": { "type": "http", "scheme": "basic" }
}
- Set a global default requiring auth:
"security": [{ "Bearer": [] }]
-
Override with
"security": []at the operation level only for genuinely public endpoints (health checks, SAML metadata, login). -
Annotate JAX-RS resource methods with
@SecurityRequirementso the spec stays accurate as new endpoints are added.
Acceptance Criteria
-
securitySchemesblock present incomponentswith Bearer JWT and BasicAuth schemes - Global
securitydefaults to[{ "Bearer": [] }] - Genuinely public endpoints (health, auth, SAML metadata) explicitly declare
security: [] - Automated security scanner against the spec produces zero false-positive "unauthenticated endpoint" findings for admin/protected resources
-
@SecurityRequirementannotations added to all JAX-RS resource classes
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.