some-tests-for-review-tuning#35707
Conversation
When a Sender sends bundles to a Receiver, it continuously checks the Receiver’s status by sending a separate request for each bundle. This means that if there are N bundles pending to be processed on the Receiver, the Sender sends N individual requests to check their status. With this improvement, we are grouping multiple bundles into a single status request, so instead of sending one request per bundle, the Sender will send one request for a set of bundles at a time. ### Proposed Changes * Cheking bundles b set https://github.com/dotCMS/core/pull/33719/files#diff-3d6a94f7aaa4361170087db426677f8088ba0db2031d9917ec16de3e0b661630R295-R846 * Cheking in the receiver the status in the database by set too https://github.com/dotCMS/core/pull/33719/files#diff-62b24c7fa30114a95721a0b252abc433bef78a9abe5f16cdf5d38c21545e07cfR225-R247 --------- Co-authored-by: Daniel Silva <daniel.silva@dotcms.com>
❌ Issue Linking RequiredThis PR could not be linked to an issue. All PRs must be linked to an issue for tracking purposes. How to fix this:Option 1: Add keyword to PR body (Recommended - auto-removes this comment)
Why is this required?Issue linking ensures proper tracking, documentation, and helps maintain project history. It connects your code changes to the problem they solve.--- This comment was automatically generated by the issue linking workflow |
|
Claude finished @mbiuki's task in 1m 17s —— View job Rollback Safety Analysis
Verdict: ✅ Safe to RollbackChanges reviewed:
Category analysis:
After rollback, N-1 starts normally, reads/writes data using the same persisted format, and the push-publishing handshake returns to the prior (auth-required) flow that N-1 was built for. No DB state, indexed data, or wire format is altered in a way N-1 cannot interpret. Note: This evaluation is strictly about rollback safety. The security regressions (auth removal, switch from parameterized SQL to string concatenation) are concerning on their own merits but are outside the scope of rollback analysis. Label applied: |
| dc.setSQL(String.format(SELECT_ALL_BY_BUNDLES_IDS, placeholders)); | ||
| bundleIds.forEach(dc::addParam); | ||
| final List<Map<String, Object>> items = dc.loadObjectResults(); | ||
| dc.setSQL(String.format(SELECT_ALL_BY_BUNDLES_IDS, String.join(",", parameter))); |
There was a problem hiding this comment.
Semgrep identified a blocking 🔴 issue in your code:
SQL query is built with String.format and unescaped bundleIds, allowing a crafted ID with a quote to break out of the string and return all rows (e.g., "x') OR 1=1 --").
More details about this
Risk: SQL built with String.format(...) uses untrusted bundleIds directly.
In getPublishAuditStatuses, bundleIds are wrapped in single quotes (parameter = bundleIds.stream().map(id -> "'" + id + "'")...) and then spliced into the query string via String.format(SELECT_ALL_BY_BUNDLES_IDS, String.join(",", parameter)). If any id contains a single quote, it breaks out of the string literal and alters the WHERE clause.
Concrete exploit
- Step 1: An attacker passes a crafted ID to any API/controller that calls getPublishAuditStatuses, e.g. bundleIds = ["x') OR 1=1 --"].
- Step 2: parameter becomes ["'x') OR 1=1 --'"] and the final SQL (assuming SELECT_ALL_BY_BUNDLES_IDS ≈ "... WHERE bundle_id IN (%s)") becomes:
... WHERE bundle_id IN ('x') OR 1=1 --') - Step 3: dc.setSQL(...) followed by dc.loadObjectResults() executes this query, making OR 1=1 true and returning all rows from the audit table (bypassing intended filters and exposing data).
Because the query is assembled from raw strings (SELECT_ALL_BY_BUNDLES_IDS + String.join on parameter) instead of using parameters, a malicious bundleId can inject SQL and control the query outcome.
To resolve this comment:
✨ Commit fix suggestion
| dc.setSQL(String.format(SELECT_ALL_BY_BUNDLES_IDS, String.join(",", parameter))); | |
| // Build a list of '?' placeholders based on the number of bundleIds | |
| String placeholders = bundleIds.stream().map(id -> "?").collect(Collectors.joining(",")); | |
| String newQueryString = String.format(SELECT_ALL_BY_BUNDLES_IDS, placeholders); | |
| dc.setSQL(newQueryString); | |
| // Safely bind each bundleId to a parameter in the query | |
| for (String id : bundleIds) { | |
| dc.addParam(id); | |
| } |
View step-by-step instructions
- Refactor the code to use parameterized queries instead of building the SQL string with
String.formatand joining parameters. - Update the SQL in
SELECT_ALL_BY_BUNDLES_IDSto use placeholders (?) for each bundle ID, such asIN (?, ?, ?)instead of injecting the raw values. - In your method, after building the query string with the correct number of placeholders based on
bundleIds.size(), calldc.setSQL(newQueryString)without directly inserting the IDs. - Loop through each value in
bundleIdsand calldc.addParam(id)for each one to bind them safely.
For example, if bundleIds.size() is 3, your SQL should look like ... IN (?, ?, ?) and you should call dc.addParam(bundleIds.get(0)); dc.addParam(bundleIds.get(1)); ....
This prevents SQL injection by letting the database driver escape all parameters safely.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by CUSTOM_INJECTION-2.
If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.
You can view more details about this finding in the Semgrep AppSec Platform.
Sandbox PR for tuning review workflows. Not for merge.