
dotCMS Pre-auth SQL Injection
CVE-2026-8054 is a high-severity pre-authentication SQL injection vulnerability (Pre-auth SQL Injection) in the dotCMS Core Publish Audit API. The vulnerability is officially tracked as Security Incident SI-75 and was formally disclosed at the end of May 2026. Since attackers can trigger it remotely without any account privileges, its potential impact is extremely severe.
| Attribute | Value |
|---|---|
| CVE ID | CVE-2026-8054 |
| Official Tracking | SI-75 |
| Vulnerability Type | SQL Injection (CWE-89) |
| Affected Component | dotCMS Core - Publish Audit API |
| CVSS Score | 10.0 (Critical) |
| Affected Versions | 25.11.04-1 to 26.04.28-02 |
| Fixed Version | 26.04.28-03 |
| Attack Vector | Remote unauthenticated SQL injection (Pre-auth) |
| Required Privileges | None |
| User Interaction | None |
| LTS Version Impact | Not affected (the audit code branch was not backported to the LTS tree) |
The vulnerability exists in two REST endpoints: /api/auditPublishing/get and /api/auditPublishing/getAll. When these endpoints receive request parameters from clients, no filtering or parameterized binding is performed; instead, SQL queries are dynamically constructed via direct string concatenation.
More critically, dotCMS completely omitted authentication and authorization checks on these sensitive audit-related backend endpoints. This means any unauthenticated remote attacker from the external network who can reach the system over the network can directly send HTTP requests containing malicious payloads to these endpoints.
File Path: dotCMS/src/main/java/com/dotcms/rest/AuditPublishingResource.java
The vulnerability exists in two REST API endpoints:
GET /api/auditPublishing/get/{bundleId} - Retrieve a single publish audit statusPOST /api/auditPublishing/getAll - Retrieve publish audit statuses in bulkKey Issue: Before the fix, these two endpoints required no authentication, and any anonymous user could access them directly.
@Path("/auditPublishing")
@Tag(name = "Publishing")
public class AuditPublishingResource {
@POST
@Path("/getAll")
@Produces(MediaType.APPLICATION_JSON)
public Response getAll(List<String> bundleIds) {
// [Vulnerability Point] No authentication check! Directly calls the backend API
try {
final List<PublishAuditStatus> statuses = auditAPI.getPublishAuditStatuses(bundleIds);
// ...
}
}
}
File Path: dotCMS/src/main/java/com/dotcms/publisher/business/PublishAuditAPIImpl.java
Method: getPublishAuditStatuses(List<String> bundleIds) (Lines 224-245)
@CloseDBIfOpened
public List<PublishAuditStatus> getPublishAuditStatuses(List<String> bundleIds)
throws DotPublisherException {
try {
final List<PublishAuditStatus> result = new ArrayList<>();
DotConnect dc = new DotConnect();
// [Vulnerability Point 1] Directly concatenates user input into the SQL statement
// Only wraps with single quotes, no parameterization or escaping
final List<String> parameter = bundleIds.stream()
.map(id -> "'" + id + "'") // Dangerous: string concatenation
.collect(Collectors.toList());
// [Vulnerability Point 2] Uses String.format to construct SQL, user input is directly embedded
dc.setSQL(String.format(SELECT_ALL_BY_BUNDLES_IDS,
String.join(",", parameter)));
List<Map<String, Object>> items = dc.loadObjectResults();
for(Map<String, Object> item: items) {
result.add(turnIntoPublishAuditStatus(NO_LIMIT_ASSETS, item));
}
return result;
} catch(Exception e) {
Logger.debug(PublisherUtil.class, e.getMessage(), e);
throw new DotPublisherException("Unable to get list of elements with error:" + e.getMessage(), e);
}
}
SQL Constant (SELECT_ALL_BY_BUNDLES_IDS):
SELECT * FROM publishing_queue_audit WHERE bundle_id IN (%s)
graph LR
subgraph External Attacker
A[Remote Attacker] -->|sends malicious payload| B[HTTP REST API]
end
subgraph Application Layer
B -->|POST /api/auditPublishing/getAll| C[AuditPublishingResource<br/>GET/POST]
C -->|calls| D[PublishAuditAPI]
D -->|calls| E[PublishAuditAPIImpl]
E -->|passes bundleIds| F[Taint handling<br/>bundleIds.stream<br/>.map id -> id]
F -->|concatenates parameters| G[SQL construction<br/>String.format]
end
subgraph Technology Layer
G -->|constructs SQL| H[Dynamic SQL query<br/>SELECT * FROM publishing_queue_audit<br/>WHERE bundle_id IN %s]
H -->|executes| I[SQL execution]
I -->|executes injected SQL| J[PostgreSQL/MySQL]
end
subgraph Vulnerability Points
K[Vulnerability Point 1<br/>No authentication check] -.->|bypasses authentication| C
L[Vulnerability Point 2<br/>No parameterized binding] -.->|only adds quotes| F
end
style A fill:#ff6b6b,stroke:#333,color:#fff
style K fill:#ff6b6b,stroke:#333,color:#fff
style L fill:#ff6b6b,stroke:#333,color:#fff
style J fill:#ffa94d,stroke:#333
Taint Propagation: User input → REST API → Backend processing → SQL construction → Database execution Critical Flaws: No authentication + No parameterization = Fully controllable SQL injection
Assume user input bundleIds = ["x' OR '1'='1"]
Normal SQL:
SELECT * FROM publishing_queue_audit WHERE bundle_id IN ('normal-id')
Injected SQL:
SELECT * FROM publishing_queue_audit WHERE bundle_id IN ('x' OR '1'='1')
Since '1'='1' is always true, this query returns all records in the table.
graph TD
subgraph Input Comparison
A[Normal input<br/>bundle-123] -->|constructs| B[Normal SQL<br/>WHERE bundle_id IN<br/>'bundle-123']
C[Malicious input<br/>x OR 1=1] -->|injects| D[Injected SQL<br/>WHERE bundle_id IN<br/>x OR 1=1]
end
subgraph Database Execution
B -->|executes| E[Database]
D -->|executes| E
end
subgraph Result Comparison
E -->|returns| F[Normal result<br/>1 record]
E -->|returns data leak| G[Leaked result<br/>All records]
end
style C fill:#ff6b6b,stroke:#333,color:#fff
style G fill:#ff6b6b,stroke:#333,color:#fff
style D fill:#ff6b6b,stroke:#333,color:#fff
note1[Injection point: single quote closes the original string<br/>OR 1=1 makes the condition always true<br/>Result: all records returned]
An attacker who successfully exploits this vulnerability can execute arbitrary SQL commands in the context of the database system user, leading to the following severe consequences:
graph TD
subgraph Attack Impact Analysis
subgraph Data Confidentiality
A[Admin password hashes]
B[User credentials]
C[Reset tokens]
D[System configuration]
end
subgraph Data Integrity
E[Website content]
F[User roles and permissions]
G[Audit logs]
end
subgraph System Availability
H[DROP TABLE]
I[DELETE data]
J[UPDATE data]
end
subgraph Privilege Escalation
K[Admin takeover]
L[Filesystem read/write]
M[Remote code execution]
end
end
N[SQL injection vulnerability] -->|leaks| A
N -->|leaks| B
N -->|leaks| C
N -->|tampers| E
N -->|tampers| F
N -->|executes| H
N -->|achieves| K
N -->|achieves| L
O[CVSS 10.0 Critical] -.->|assesses| N