Skip to content

fix(page): exclude archived content from page render and content save (#35993)#36215

Queued
dsolistorres wants to merge 8 commits into
mainfrom
issue-35993-archived-content-render
Queued

fix(page): exclude archived content from page render and content save (#35993)#36215
dsolistorres wants to merge 8 commits into
mainfrom
issue-35993-archived-content-render

Conversation

@dsolistorres

Copy link
Copy Markdown
Member

Problem

When a contentlet placed in a container on a page is archived, it continues to render in the page's Edit and Preview modes (UVE). It is correctly hidden in Live mode. Archived content should not appear on a page in any mode.

A secondary symptom: while an archived contentlet remains in a container, removing a different (non-archived) contentlet from the page hangs ("spinning forever"). The browser POST to /api/v1/page/{id}/content returns 400 "Can't find contentlet: <archived-id>".

Root cause

Archiving sets deleted = true on the version info but keeps the working inode. In Edit/Preview PageMode.showLive = false, so PageRenderUtil.getSpecificContentlet() resolves find(workingInode) and returns the archived working version — and populateContainers() only guarded against null. Live mode has no live version, so it was already excluded.

Because the archived content kept rendering, it stayed in the editor's page model. On save (a full replacement), PageResource.validateContainerEntries() looked the id up via findContentletByIdentifierAnyLanguageAnyVariant(), which filters out deleted versions, found nothing, and threw DotContentletStateException — a RuntimeException not caught by the method's catch (DotDataException) — surfacing as HTTP 400 and hanging the remove.

Changes

  • PageRenderUtil.populateContainers() — skip contentlet.isArchived() in all modes, consistent with Live-mode behavior. (Also corrected a stray DotStateException import.)
  • PageResource.validateContainerEntries() — skip archived content instead of throwing the 400, so a page that still references archived content remains saveable (the content gets removed on save).
  • PageResourceTest — new self-validating integration test testArchivedContentNotRenderedInEditAndPreviewMode: renders the page before archiving (asserts the content shows, count == 1), archives, then asserts count == 0 in PREVIEW and EDIT modes.

Testing

  • Bug reproduced on unfixed core: the new test failed expected:<0> but was:<1>.
  • With the fix: the test passes.
  • Full PageResourceTest (31 tests) passes against the fixed core with the build cache disabled.

⚠️ Reviewer note — intentional behavior change in page-save semantics

After this fix, archived content no longer renders on a page in any mode. Because the page-save endpoint (POST /api/v1/page/{id}/content) is a full replacement of each container's content, the next time a user saves the page the archived content — no longer in the editor's model — is removed from the page's multi_tree association.

Implication: if that content is later unarchived, it will not automatically reappear on the page; it must be re-added manually.

We consider this acceptable (archived content shouldn't be bound to live pages), but flagging it explicitly. An alternative — preserving the multi_tree association so unarchive restores placement — was considered and rejected as more complex and less intuitive. Please confirm you're comfortable with the chosen semantics.

Refs: #35993

🤖 Generated with Claude Code

@semgrep-code-dotcms-test

Copy link
Copy Markdown
Contributor

Semgrep found 3 ssc-ace67ff4-0843-40f4-a8d3-13e050943589 findings:

  • e2e/dotcms-e2e-node/frontend/yarn.lock
  • e2e/dotcms-e2e-node/frontend/package-lock.json
  • starter/nextjs/package-lock.json

Risk: Affected versions of js-yaml are vulnerable to Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution'). js-yaml is vulnerable to prototype pollution through its YAML merge key (<<) handling. When parsing untrusted YAML with load, loadAll, safeLoad, or safeLoadAll, a crafted document containing a __proto__ key inside a merged mapping can modify the prototype of the resulting object, leading to integrity violations in the application.

Manual Review Advice: A vulnerability from this advisory is reachable if you are using js-yaml on the CLI

Fix: Upgrade this library to at least version 4.1.1 at core/e2e/dotcms-e2e-node/frontend/yarn.lock:709.

Reference(s): GHSA-mh29-5h37-fv8m, CVE-2025-64718

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-f748ff04-95cd-4e1a-8429-2525a723a048 finding:

Risk: Affected versions of webpack are vulnerable to Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'). webpack's AutoPublicPathRuntimeModule emits runtime code that derives the asset public path from document.currentScript. Because document.currentScript can be DOM-clobbered by an injected scriptless element (e.g. ), an attacker who can inject markup into the page can redirect dynamic chunk/asset loading to an attacker-controlled origin, resulting in cross-site scripting (XSS).

Manual Review Advice: A vulnerability from this advisory is reachable if you build with output.publicPath unset

Fix: Upgrade this library to at least version 5.94.0 at core/core-web/pnpm-lock.yaml:14501.

Reference(s): GHSA-4vvj-4cpr-p986, CVE-2024-43788

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-3e1ecbe7-8bda-4e4e-9998-2b480eb17f61 findings:

Risk: Affected versions of next are vulnerable to Improper Input Validation. Next.js Image Optimization (/_next/image) is affected by two related vulnerabilities fixed together. First, regardless of configuration, the image optimizer forwarded all inbound request headers — including cookies and authentication tokens — to internal API routes when proxying relative image URLs (e.g. /_next/image? url=/api/private-endpoint), allowing an attacker to make authenticated requests to internal endpoints through the public image optimizer. Second, if images.domains or images.remotePatterns is configured, an attacker who controls an allowed external image server can serve non-image content that bypasses magic-number content-type detection; Next.js falls back to the upstream Content-Type header and serves the arbitrary payload as a file download under the trusted application domain — enabling phishing, drive-by downloads, or malicious file delivery.

Manual Review Advice: A vulnerability from this advisory is reachable if you are using next/image and either images.domains or images.remotePatterns

Fix: Upgrade this library to at least version 14.2.31 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-xv57-4mr9-wg8v, CVE-2025-55173

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-5b8294b2-80de-422c-9ed7-4411356927e1 findings:

Risk: Affected versions of next are vulnerable to Allocation of Resources Without Limits or Throttling. An attacker can send a Server Action request with an abruptly closed connection or invalid Content-Length header, causing the internal React Flight Reply decoder to create perpetually-pending promise chunks that never resolve. This leaves the server connection hanging indefinitely until the hosting provider times out execution, resulting in a denial of service.

Manual Review Advice: A vulnerability from this advisory is reachable if you use Server Actions ("use server") without enforcing request timeouts or connection termination

Fix: Upgrade this library to at least version 14.2.21 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-7m27-7ghc-44w9, CVE-2024-56332

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-230a1643-fcad-4f4d-99c7-d9f2709c963e finding:

Risk: Affected versions of next are vulnerable to Uncontrolled Recursion. An attacker can send a crafted HTTP request to the built-in Next.js image optimization endpoint (/_next/image) with a URL-encoded or absolute self-referential url parameter that bypasses the recursion guard, causing the server to recursively optimize images until CPU is exhausted, resulting in a Denial of Service.

Manual Review Advice: A vulnerability from this advisory is reachable if you have default image optimization configured on non Vercel hosting

Fix: Upgrade this library to at least version 14.2.7 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-g77x-44xx-532m, CVE-2024-47831

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-4759c514-b537-20ef-d52f-ebb0a5c388fa finding:

Risk: Affected versions of axios are vulnerable to Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Request/Response Splitting') / Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling') / Server-Side Request Forgery (SSRF). Axios can be used as a gadget for header injection: if another dependency enables prototype pollution, polluted properties can be merged into Axios request headers and written without CRLF sanitization, allowing request smuggling/SSRF that can reach internal services such as AWS IMDSv2 and potentially lead to credential theft or broader compromise.

Fix: Upgrade this library to at least version 1.15.0 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-fvcv-3m26-pcqx, CVE-2026-40175

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-5ea2c631-7cef-a4e0-e641-d179af079827 finding:

Risk: Affected versions of axios are vulnerable to Server-Side Request Forgery (SSRF) / Unintended Proxy or Intermediary ('Confused Deputy'). Axios does not normalize hostnames before applying NO_PROXY, so requests to loopback or internal hosts such as localhost. or [::1] can be sent through a configured proxy instead of bypassing it. If an attacker can influence request URLs, they may force local/internal Axios traffic through an attacker-controlled proxy, undermining SSRF protections and exposing sensitive responses.

Manual Review Advice: A vulnerability from this advisory is reachable if you have NO_PROXY configured in your environment

Fix: Upgrade this library to at least version 1.15.0 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-3p68-rc4w-qgx5, CVE-2025-62718

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-7ea247e6-31af-4d7a-9ff9-d6ad5a08663a finding:

Risk: Affected versions of next are vulnerable to Improper Input Validation. Next.js Image Optimization (/_next/image) is affected by two related vulnerabilities fixed together. First, regardless of configuration, the image optimizer forwarded all inbound request headers — including cookies and authentication tokens — to internal API routes when proxying relative image URLs (e.g. /_next/image? url=/api/private-endpoint), allowing an attacker to make authenticated requests to internal endpoints through the public image optimizer. Second, if images.domains or images.remotePatterns is configured, an attacker who controls an allowed external image server can serve non-image content that bypasses magic-number content-type detection; Next.js falls back to the upstream Content-Type header and serves the arbitrary payload as a file download under the trusted application domain — enabling phishing, drive-by downloads, or malicious file delivery.

Fix: Upgrade this library to at least version 14.2.31 at core/starter/nextjs/package-lock.json:3428.

Reference(s): GHSA-xv57-4mr9-wg8v, CVE-2025-55173

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-c8b7a1f2-4d36-4f0a-9e2b-1a5c8d7e6f30 findings:

Risk: Affected versions of vite and vite-plus are vulnerable to Exposure of Sensitive Information to an Unauthorized Actor / Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'). Vite's server.fs.deny blocklist—which protects sensitive files such as .env and certificate files from being served—can be bypassed on Windows using alternate path representations (NTFS Alternate Data Stream syntax like /.env::$DATA?raw, or 8.3 short filenames), allowing an attacker to read otherwise-denied files when the dev server is exposed to the network.

Manual Review Advice: A vulnerability from this advisory is reachable if you expose the Vite dev server or vite-plus to the network by configuring a non-loopback address using the --host CLI flag on Windows

Fix: Upgrade this library to at least version 7.3.5 at core/core-web/pnpm-lock.yaml:14268.

Reference(s): GHSA-fx2h-pf6j-xcff

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 7 ssc-d17d3487-883b-46a9-bec9-dee3375f7532 findings:

Risk: Affected versions of esbuild are vulnerable to Download of Code Without Integrity Check / Untrusted Search Path. esbuild's Deno distribution module (lib/deno/mod.ts) contains an import.meta.main CLI entrypoint that calls install() directly when the module is run as a script (deno run https://deno.land/x/esbuild@vX/mod.js). This download path has no SHA-256 integrity verification: if NPM_CONFIG_REGISTRY resolves to an attacker-controlled registry, the fetched binary is executed immediately, yielding arbitrary code execution without any API call in user code.

Manual Review Advice: A vulnerability from this advisory is reachable if you invoke the esbuild Deno module directly as a CLI tool (e.g. deno run https://deno.land/x/esbuild@vX/mod.js) and the NPM_CONFIG_REGISTRY environment variable resolves the binary download to an untrusted registry

Fix: Upgrade this library to at least version 0.28.1 at core/core-web/pnpm-lock.yaml:9047.

Reference(s): GHSA-gv7w-rqvm-qjhr

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-b1111744-f54b-4966-b3a9-9dfcb0019bac findings:

Risk: Affected versions of axios are vulnerable to Inefficient Regular Expression Complexity / Uncontrolled Resource Consumption. axios is vulnerable to a regular expression denial of service (ReDoS). The internal cookies.read() helper in lib/helpers/cookies.js builds a regular expression by concatenating the cookie name directly into the pattern without escaping regex metacharacters. When the cookie name flowing into the XSRF cookie read (e.g. via xsrfCookieName) contains a catastrophic-backtracking payload, evaluating the regex against document.cookie can freeze the JavaScript event loop, causing a denial of service in the browser tab or in Node.js/SSR applications. The affected code path is reached during ordinary axios request processing, so any importer of an affected version is exposed. Upgrade to a patched version (0.32.0 or 1.16.0), or set xsrfCookieName: null to disable XSRF cookie reading.

Manual Review Advice: A vulnerability from this advisory is reachable if you are using axios in browser with untrusted xsrfCookieName value

Fix: Upgrade this library to at least version 1.16.0 at core/core-web/pnpm-lock.yaml:7569.

Reference(s): GHSA-hfxv-24rg-xrqf

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-48d90ae5-e075-4c25-9ae0-d42f5bddc4bf findings:

Risk: Affected versions of axios are vulnerable to Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') / Unintended Proxy or Intermediary ('Confused Deputy'). axios reads config.proxy via prototype-chain property access, so any Object.prototype pollution elsewhere in the dependency tree silently routes all HTTP requests through an attacker-controlled proxy, yielding a full man-in-the-middle. The vulnerability fires on ordinary axios usage with no specific API call or configuration required; upgrade to axios 1.16.0 or later.

Manual Review Advice: A vulnerability from this advisory is reachable if you use axios to make HTTP requests

Fix: Upgrade this library to at least version 1.16.0 at core/core-web/pnpm-lock.yaml:7569.

Reference(s): GHSA-35jp-ww65-95wh, GHSA-fvcv-3m26-pcqx, CVE-2026-44494

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-32ffb194-64c6-4f38-85ba-9a400470a6e3 findings:

Risk: Affected versions of next are vulnerable to Allocation of Resources Without Limits or Throttling. An attacker can send a specially crafted HTTP request to any Next.js App Router Server Function endpoint that, when deserialized by the underlying React Server Components (Flight) runtime, triggers excessive CPU usage—exhausting the server process and resulting in a denial-of-service. The vulnerable deserialization path is part of the App Router's HTTP route handler, so any Next.js application that uses the App Router is reachable without any explicit Server Action declared in user code.

Manual Review Advice: A vulnerability from this advisory is reachable if you are using the App Router

Fix: Upgrade this library to at least version 15.5.16 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-8h8q-6873-q5fj, CVE-2026-23870

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-6bbb4d45-83de-4087-b832-29537bbc28a1 findings:

Risk: Affected versions of next are vulnerable to Server-Side Request Forgery (SSRF). Next.js's standalone router-server forwards WebSocket Upgrade requests to whatever host and port the attacker supplies in an absolute-URL request-line or Host header, because the upgrade handler in packages/next/src/server/lib/router-server.ts invokes proxyRequest(...) whenever parsedUrl.protocol is set, without the finished/statusCode guards that protect normal HTTP requests. An unauthenticated attacker can therefore make a self-hosted Next.js process open an outbound TCP connection to arbitrary internal or external destinations (e.g. cloud metadata endpoints such as 169.254.169.254, internal Redis/Elasticsearch/Kubelet, etc.), enabling SSRF and potential cloud credential theft. Upgrade to next@15.5.16 or next@16.2.5.

Manual Review Advice: A vulnerability from this advisory is reachable if you self-host Next.js application instead of Vercel-hosted deployments

Fix: Upgrade this library to at least version 15.5.16 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-c4j6-fc7j-m34r, CVE-2026-44578

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-1a0f4ed6-c2fe-47ec-b275-9bdc1073cf2e finding:

Risk: Affected versions of @babel/plugin-transform-modules-systemjs are vulnerable to Access of Resource Using Incompatible Type ('Type Confusion') / Improper Control of Generation of Code ('Code Injection'). @babel/plugin-transform-modules-systemjs mishandles module string specifiers when compiling code into SystemJS module form. An attacker who can supply specially crafted source code to a Babel build that uses this plugin (directly, or transitively via @babel/preset-env configured with modules: "systemjs") can cause Babel to emit output that executes arbitrary JavaScript when the compiled bundle is loaded.

Manual Review Advice: A vulnerability from this advisory is reachable if you install @babel/plugin-transform-modules-systemjs plugin through cli, or use @babel/preset-env preset with modules set to "systemjs"

Fix: Upgrade this library to at least version 7.29.4 at core/core-web/pnpm-lock.yaml:1664.

Reference(s): GHSA-fv7c-fp4j-7gwp, CVE-2026-44728

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-6efe528b-deef-ace9-810a-467438938c20 findings:

Risk: Affected versions of next are vulnerable to Allocation of Resources Without Limits or Throttling. A specially crafted HTTP request to a Next.js App Router Server Function endpoint can trigger excessive CPU consumption during React Server Components deserialization, leading to denial of service.

Fix: Upgrade this library to at least version 15.5.15 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-q4gf-8mx6-v5v3

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-1289c362-ab31-4c96-bd5e-5e444f4fb067 finding:

Risk: Affected versions of vite are vulnerable to Exposure of Sensitive Information to an Unauthorized Actor / Missing Authentication for Critical Function. This occurs because the Vite Dev Server WebSocket improperly exposes the fetchModule method, allowing unauthenticated remote attackers to bypass filesystem restrictions and read arbitrary files from the host machine

Manual Review Advice: A vulnerability from this advisory is reachable if you enable vite dev server using --host flag and websocket is not disabled

Fix: Upgrade this library to at least version 7.3.2 at core/core-web/pnpm-lock.yaml:14228.

Reference(s): GHSA-p9ff-h696-f583, CVE-2026-39363

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-81d0f8fa-e5f9-414f-a539-fa38f9590a35 finding:

Risk: Affected versions of vite are vulnerable to Improper Access Control / Incorrect Behavior Order. Vite's dev server can bypass server.fs.deny protections: if the server is exposed to the network and a denied file is within an allowed directory, an attacker can retrieve sensitive files such as .env or certificate files by requesting them with query parameters like ?raw, ?import&raw, or ?import&url&inline.

Manual Review Advice: A vulnerability from this advisory is reachable if you enable vite dev server using --host flag and have sensitive data in deny list

Fix: Upgrade this library to at least version 7.3.2 at core/core-web/pnpm-lock.yaml:14228.

Reference(s): GHSA-v2wj-q39q-566r, CVE-2026-39364

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-7655e34f-47d3-43f6-b687-32e02f3c8005 findings:

  • dotcms-postman/pnpm-lock.yaml
  • .github/scripts/gather-release-data/package-lock.json

Risk: Affected versions of handlebars are vulnerable to Improper Control of Generation of Code ('Code Injection') / Improper Encoding or Escaping of Output / Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'). The Handlebars CLI precompiler allows arbitrary JavaScript injection by embedding unescaped template filenames and CLI option values such as --namespace, --commonjs, and --handlebarPath directly into generated output. An attacker who can control these inputs can cause malicious code to execute when the precompiled bundle is loaded in Node.js or a browser.

Manual Review Advice: A vulnerability from this advisory is reachable if you execute templates through the Handlebars CLI precompiler

Fix: Upgrade this library to at least version 4.7.9 at core/dotcms-postman/pnpm-lock.yaml:197.

Reference(s): GHSA-xjpj-3mr7-gcpf, CVE-2026-33941

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-6193c409-cebc-449c-8a55-f95fa9d0e4f0 finding:

Risk: Affected versions of rollup are vulnerable to Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'). Rollup is vulnerable to arbitrary file write via path traversal: chunk/asset names derived from user-controlled inputs (e.g., CLI named inputs, manual chunk aliases, or malicious plugins) are insufficiently sanitized, allowing ../ sequences to survive and be passed into path.resolve when computing output paths. This lets an attacker escape the configured output directory and overwrite arbitrary files on the host filesystem that the build process can write to, potentially leading to persistent RCE by clobbering shell/profile or other executable/config files.

Manual Review Advice: A vulnerability from this advisory is reachable if you are running rollup --input

Fix: Upgrade this library to at least version 4.59.0 at core/core-web/pnpm-lock.yaml:12935.

Reference(s): GHSA-mw96-cpmx-2vgc, CVE-2026-27606

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-d1b4e9e7-4dae-4218-8bb1-046e9a0b7e60 findings:

Risk: Affected versions of next are vulnerable to Deserialization of Untrusted Data / Uncontrolled Resource Consumption. A flaw in React Server Components' deserialization allows an attacker to send a specially crafted HTTP request to any App Router Server Function endpoint in Next.js, triggering excessive CPU usage, out-of-memory conditions, or a server crash and resulting in a denial of service.

Fix: Upgrade this library to at least version 15.0.8 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-h25m-26qc-wcjf, CVE-2026-23864

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-b94a740c-3b13-43fd-9f2d-4d8bb0fe0b69 findings:

Risk: Affected versions of next are vulnerable to Dependency on Vulnerable Third-Party Component / Deserialization of Untrusted Data / Uncontrolled Resource Consumption. An attacker can send a specially crafted HTTP request to any Server Function endpoint (as used by Next.js' App Router) that, when deserialized by the React Server Components runtime, enters an infinite loop—hanging the server process, exhausting CPU, and resulting in a denial-of-service.

Fix: Upgrade this library to at least version 14.2.35 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-5j59-xgg2-r9c4, CVE-2025-67779

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-74b4cbd5-76e9-40fe-adb6-38be9f569d24 findings:

Risk: Affected versions of next are vulnerable to Dependency on Vulnerable Third-Party Component / Deserialization of Untrusted Data / Uncontrolled Resource Consumption. A flaw in Next.js's App Router deserialization allows an attacker to send a specially crafted HTTP request body that, when parsed by the server, triggers excessive CPU work or an infinite loop. By targeting any App Router endpoint with this malicious payload, the server process can hang and become unresponsive, resulting in a denial-of-service.

Fix: Upgrade this library to at least version 14.2.34 at core/core-web/pnpm-lock.yaml:11425.

Reference(s): GHSA-mwv6-3258-q52c

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 ssc-b23ea775-7f2b-4ba0-ba7c-ab6963c325a7 findings:

  • e2e/dotcms-e2e-node/frontend/yarn.lock
  • e2e/dotcms-e2e-node/frontend/package-lock.json

Risk: Affected versions of playwright are vulnerable to Improper Verification of Cryptographic Signature. The macOS browser reinstall scripts in Playwright use curl -k to fetch installer packages without any SSL certificate validation, allowing a man-in-the-middle attacker to serve a trojaned browser installer that's run with elevated privileges, resulting in full system compromise.

Fix: Upgrade this library to at least version 1.55.1 at core/e2e/dotcms-e2e-node/frontend/yarn.lock:904.

Reference(s): GHSA-7mvr-c777-76hp, CVE-2025-59288

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-cee3e6d5-d7c8-4c35-9815-076aa1ebfd49 finding:

Risk: Affected versions of rollup are vulnerable to Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').

Manual Review Advice: A vulnerability from this advisory is reachable if you use Rollup to bundle JavaScript with import.meta.url and the output format is set to cjs, umd, or iife formats, while allowing users to inject scriptless HTML elements with unsanitized name attributes

Fix: Upgrade this library to at least version 4.22.4 at core/core-web/pnpm-lock.yaml:12935.

Reference(s): GHSA-gcx4-mw62-g8wm, CVE-2024-47068

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-ae712be7-ba83-46c3-ab40-8fa825e85e2e finding:

  • core-web/libs/portlets/dot-experiments/portlet/src/lib/shared/ui/dot-experiments-header/dot-experiments-ui-header.component.spec.ts

Risk: Affected versions of @angular/common are vulnerable to Inefficient Regular Expression Complexity / Uncontrolled Resource Consumption. @angular/common's formatDate() function and DatePipe do not bound the length of the date format string before parsing it in an inefficient regular-expression loop. When the format argument is derived from untrusted input, an attacker can supply an excessively long format string to exhaust CPU and memory, causing a denial of service (heap exhaustion during SSR or main-thread freezes in the browser).

Fix: Upgrade this library to at least version 21.2.17 at core/core-web/pnpm-lock.yaml:1117.

Reference(s): GHSA-48r7-hpm6-gfxm

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-7dd7535a-bf1d-4a2a-8d67-c4c29cf693be finding:

  • core-web/libs/sdk/create-app/src/utils/index.ts

Risk: Affected versions of axios are vulnerable to Insertion of Sensitive Information Into Sent Data. The Node.js HTTP adapter in axios fails to clear the Proxy-Authorization header when a request that initially used an authenticated HTTP proxy is redirected to a target requiring no proxy (e.g. an HTTP-to-HTTPS redirect with no HTTPS proxy configured), leaking the proxy credentials to the final origin server.

Manual Review Advice: A vulnerability from this advisory is reachable if you make requests with the Node.js HTTP adapter through an authenticated HTTP proxy with redirect following enabled

Fix: Upgrade this library to at least version 1.16.0 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-p92q-9vqr-4j8v, CVE-2026-44487

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-d8334024-f7b2-46ef-8644-adcdebb058c3 finding:

  • core-web/libs/sdk/create-app/src/utils/index.ts

Risk: Affected versions of axios are vulnerable to Exposure of Sensitive Information to an Unauthorized Actor. The Node.js HTTP adapter in axios leaks authenticated proxy credentials when following redirects. When a request traverses an authenticated proxy and is then redirected to a URL that no longer routes through that proxy, the stale Proxy-Authorization header persists on the redirected request and is exposed to the (potentially attacker-controlled) redirect target, disclosing the proxy username and password. Upgrade to axios >= 1.16.0 or >= 0.32.0.

Manual Review Advice: A vulnerability from this advisory is reachable if you use axios in Node.js with an authenticated proxy configured via the proxy request or instance config option, or via the HTTP_PROXY/HTTPS_PROXY environment variables

Fix: Upgrade this library to at least version 1.16.0 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-j5f8-grm9-p9fc, CVE-2026-44486

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-b3dae150-a0f3-42d9-9b5a-121eb4330524 finding:

  • core-web/libs/sdk/create-app/src/utils/index.ts

Risk: Affected versions of axios are vulnerable to Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') / Unintended Proxy or Intermediary ('Confused Deputy'). axios reads config.proxy via prototype-chain property access, so any Object.prototype pollution elsewhere in the dependency tree silently routes all HTTP requests through an attacker-controlled proxy, yielding a full man-in-the-middle. The vulnerability fires on ordinary axios usage with no specific API call or configuration required; upgrade to axios 1.16.0 or later.

Fix: Upgrade this library to at least version 1.16.0 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-35jp-ww65-95wh, GHSA-fvcv-3m26-pcqx, CVE-2026-44494

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-cc89d850-fe0d-4017-bd56-94cb42248142 finding:

  • core-web/libs/sdk/create-app/src/utils/index.ts

Risk: Affected versions of axios are vulnerable to Improper Control of Generation of Code ('Code Injection') / Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution'). axios contains a prototype-pollution gadget in its config merge pipeline. If Object.prototype.transformResponse is polluted by a separate vulnerability in the same process, mergeConfig() reads it through the prototype chain and transformData() executes the inherited function on every HTTP response body, enabling credential theft and response hijacking. Every axios HTTP call traverses this pipeline. Upgrade to 0.31.1, 1.15.2, or later.

Fix: Upgrade this library to at least version 1.15.2 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-3g43-6gmg-66jw, CVE-2026-44495

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-79676c2c-3b3e-4d9a-8d60-79fbeed0c37b finding:

  • core-web/libs/sdk/create-app/src/utils/index.ts

Risk: Affected versions of axios are vulnerable to Server-Side Request Forgery (SSRF). Axios does not normalize IPv4-mapped IPv6 addresses before applying NO_PROXY, so a request whose host is given in IPv4-mapped IPv6 notation (e.g. ::ffff:7f00:1 or ::ffff:a9fe:a9fe) is not matched against the IPv4 entries in NO_PROXY (e.g. 127.0.0.1 or 169.254.169.254) and is sent through the configured proxy instead of bypassing it. If an attacker can influence request URLs, they may force internal/loopback Axios traffic through a configured proxy, undermining SSRF protections and exposing sensitive responses such as cloud metadata. This is an incomplete fix for CVE-2025-62718.

Manual Review Advice: A vulnerability from this advisory is reachable if you have a proxy configured via HTTP_PROXY/HTTPS_PROXY and an IPv4 address listed in NO_PROXY

Fix: Upgrade this library to at least version 1.16.0 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-pjwm-pj3p-43mv, CVE-2025-62718

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-502ed9ad-fab6-4fe6-8236-a1b300c72e40 finding:

  • core-web/libs/sdk/create-app/src/utils/index.ts

Risk: Affected versions of axios are vulnerable to Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution'). An attacker can exploit this vulnerability by polluting Object.prototype via another dependency in the same Node.js process. Axios reads five HTTP adapter config properties (auth, baseURL, socketPath, beforeRedirect, insecureHTTPParser) via direct property access without hasOwnProperty guards, so the polluted values are silently picked up on every outbound HTTP request. This enables Authorization header credential injection, request hijacking via baseURL redirection, SSRF to internal Unix sockets, attacker-controlled callback execution during redirects, and request smuggling via the insecure HTTP parser.

Fix: Upgrade this library to at least version 1.15.2 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-q8qp-cvcw-x6jj, CVE-2026-42264

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-5f713042-7303-4a02-a5b0-d9f782c56ab6 finding:

  • core-web/libs/sdk/create-app/src/utils/index.ts

Risk: Affected versions of axios are vulnerable to Permissive List of Allowed Inputs / Server-Side Request Forgery (SSRF) / Unintended Proxy or Intermediary ('Confused Deputy'). Axios contains an incomplete fix for CVE-2025-62718. The internal isLoopback() helper in lib/helpers/shouldBypassProxy.js recognizes only 127.0.0.1, localhost, and ::1 as loopback addresses, instead of the entire 127.0.0.0/8 subnet defined by RFC 1122 §3.2.1.3. Any Node-side Axios request whose target host falls in 127.0.0.0/8 but is not literally 127.0.0.1 (e.g., 127.0.0.2, 127.1.2.3) is silently routed through the configured HTTP_PROXY/HTTPS_PROXY even when NO_PROXY is set to exempt loopback. This enables SSRF, exposes internal/loopback-bound services (e.g., sidecars, metrics agents, vault helpers listening on 127.0.0.X), and may leak request headers, credentials, and bodies to a proxy intermediary.

Manual Review Advice: A vulnerability from this advisory is reachable if you use Axios in Node.js with a proxy configured via the HTTP_PROXY/HTTPS_PROXY environment variables or the proxy request/instance config option, and NO_PROXY is set to exempt loopback addresses

Fix: Upgrade this library to at least version 1.15.1 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-pmwg-cvhr-8vh7, CVE-2026-42043

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-2a506dbb-3601-436d-bfc2-9fc4045c8f4f finding:

  • core-web/libs/sdk/create-app/src/utils/index.ts

Risk: Affected versions of axios are vulnerable to Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution'). Axios do not guard reads of config properties (e.g. parseReviver, transport, transformRequest, transformResponse) with hasOwnProperty checks. If Object.prototype is polluted by any co-dependency in the same Node.js process, an attacker can install gadgets that silently tamper with and exfiltrate JSON response bodies, or fully hijack the underlying HTTP transport to capture request URLs, headers, and basic-auth credentials.

Fix: Upgrade this library to at least version 1.15.1 at core/core-web/pnpm-lock.yaml:7566.

Reference(s): GHSA-pf86-5x62-jrwf, CVE-2026-42033

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-c45b5a59-f3fa-45d7-b31d-0b048412a07c finding:

Risk: Affected versions of vitest are vulnerable to Missing Authorization. When the Vitest UI server is listening, the deprecated isFileServingAllowed check is applied without normalizing the URL before filesystem operations, allowing path traversal that lets an attacker read, write, and execute arbitrary files outside the project directory.

Manual Review Advice: A vulnerability from this advisory is reachable if you run the Vitest UI on Windows, or you expose the Vitest UI server to the network with the --api.host flag or api.host config option

Fix: Upgrade this library to at least version 4.1.0 at core/core-web/pnpm-lock.yaml:14308.

Reference(s): GHSA-5xrq-8626-4rwp

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 ssc-37ae9e0a-cbf0-4910-8f73-04f2275899a6 finding:

Risk: webpack 5.x before 5.76.0 is vulnerable to Improper Access Control due to ImportParserPlugin.js mishandling the magic comment feature. Due to this, webpack does not avoid cross-realm object access and an attacker who controls a property of an untrusted object can obtain access to the real global object.

Manual Review Advice: A vulnerability from this advisory is reachable if you host an application utilizing webpack and an attacker can control a property of an untrusted object

Fix: Upgrade this library to at least version 5.76.0 at core/core-web/pnpm-lock.yaml:14501.

Reference(s): GHSA-hc6q-2mpp-qw7j, CVE-2023-28154

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

@semgrep-code-dotcms-test

Copy link
Copy Markdown
Contributor

Semgrep found 344 CUSTOM_INJECTION-2 findings:

The method identified is susceptible to injection. The input should be validated and properly
escaped.

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 skill-excessive-autonomy finding:

  • .claude/skills/dotcms-github-issues/SKILL.md

Skill instructs the agent to act autonomously without user confirmation, approval, or oversight. Excessive autonomy directives suppress the human-in-the-loop checkpoints that prevent unintended or malicious actions. In legitimate automation skills, user confirmation should be scoped narrowly; blanket directives to "act without asking" are a common pattern in malicious skills that aim to execute payloads silently. Review the full skill context.

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 hooks-unquoted-variable-bash-eval finding:

  • docker/docker-compose-examples/experiments/start-experiments.sh

Use of eval in a Claude Code or Cursor hook script is dangerous. The eval command re-parses its arguments, which can lead to command injection if any variable contains special characters or attacker-controlled data. Avoid eval entirely; use arrays, direct command invocation, or other safe alternatives.

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 hooks-relative-script-path-bash finding:

  • docker/docker-compose-examples/lgtm-observability/run-prometheus-custom.sh

Relative path used for script invocation in hook. Use absolute paths or environment variables like $CLAUDE_PROJECT_DIR, $CURSOR_PROJECT_DIR, or $HOME to ensure the correct script is executed regardless of working directory.

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 6 github-script-injection findings:

Using variable interpolation ${{...}} with github context data in a actions/github-script's script: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 2 request-url-express findings:

  • e2e/dotcms-e2e-node/frontend/src/requests/pages.ts
  • core-web/apps/dotcms-ui-e2e/src/requests/pages.ts

Untrusted input might be used to build an HTTP request, which can lead to a Server-side request forgery (SSRF) vulnerability. SSRF allows an attacker to send crafted requests from the server side to other internal or external systems. SSRF can lead to unauthorized access to sensitive data and, in some cases, allow the attacker to control applications or systems that trust the vulnerable service. To prevent this vulnerability, avoid allowing user input to craft the base request. Instead, treat it as part of the path or query parameter and encode it appropriately. When user input is necessary to prepare the HTTP request, perform strict input validation. Additionally, whenever possible, use allowlists to only interact with expected, trusted domains.

View Dataflow Graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>e2e/dotcms-e2e-node/frontend/src/requests/pages.ts</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L70 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 70] request</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L79 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 79] responseData</a>"]

            v3["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L79 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 79] await</a>"]

            v4["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L70 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 70] response</a>"]

            v5["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L70 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 70] await</a>"]

            v6["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L89 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 89] getActionsByContentlet</a>"]

            v7["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L89 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 89] await</a>"]

            v8["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L89 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 89] actions</a>"]

            v9["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L90 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 90] action</a>"]

            v10["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L92 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 92] executeAction</a>"]

            v11["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L44 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 44] actionId</a>"]

            v12["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L47 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 47] endpoint</a>"]
        end
            v2 --> v3
            v3 --> v4
            v4 --> v5
            v5 --> v6
            v6 --> v7
            v7 --> v8
            v8 --> v9
            v9 --> v10
            v10 --> v11
            v11 --> v12
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/e2e/dotcms-e2e-node/frontend/src/requests/pages.ts#L48 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 48] endpoint</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink

Loading

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 detect-vercelai finding:

  • core-web/apps/ai-evals/src/harness.ts

Found usage of Vercel's AI product.

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 spring-tainted-path-traversal finding:

  • dotCMS/src/main/java/com/dotcms/rest/ContentResource.java

The application builds a file path from potentially untrusted data, which can lead to a path traversal vulnerability. An attacker can manipulate the path which the application uses to access files. If the application does not validate user input and sanitize file paths, sensitive files such as configuration or user data can be accessed, potentially creating or overwriting files. To prevent this vulnerability, validate and sanitize any input that is used to create references to file paths. Also, enforce strict file access controls. For example, choose privileges allowing public-facing applications to access only the required files. In Java, you may also consider using a utility method such as org.apache.commons.io.FilenameUtils.getName(...) to only retrieve the file name from the path.

View Dataflow Graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>dotCMS/src/main/java/com/dotcms/rest/ContentResource.java</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1424 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1424] multipart</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1424 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1424] multipart</a>"]

            v3["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1428 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1428] multipartPUTandPOST</a>"]

            v4["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1484 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1484] multipart</a>"]

            v5["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1499 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1499] part</a>"]

            v6["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1499 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1499] part</a>"]

            v7["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1581 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1581] processFile</a>"]

            v8["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1613 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1613] part</a>"]

            v9["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1616 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1616] badFileName</a>"]

            v10["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1617 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1617] filename</a>"]
        end
            v2 --> v3
            v3 --> v4
            v4 --> v5
            v5 --> v6
            v6 --> v7
            v7 --> v8
            v8 --> v9
            v9 --> v10
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1632 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1632] tmpFolder.getAbsolutePath() + File.separator + filename</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink

Loading

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 tainted-file-path finding:

Detected user input controlling a file path. An attacker could control the location of this file, to include going backwards in the directory with '../'. To address this, ensure that user-controlled variables in file paths are sanitized. You may also consider using a utility method such as org.apache.commons.io.FilenameUtils.getName(...) to only retrieve the file name from the path.

View Dataflow Graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>dotCMS/src/main/java/com/dotcms/rest/ContentResource.java</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1424 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1424] multipart</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1424 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1424] multipart</a>"]

            v3["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1428 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1428] multipartPUTandPOST</a>"]

            v4["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1484 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1484] multipart</a>"]

            v5["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1499 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1499] part</a>"]

            v6["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1499 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1499] part</a>"]

            v7["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1581 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1581] processFile</a>"]

            v8["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1613 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1613] part</a>"]

            v9["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1616 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1616] badFileName</a>"]

            v10["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1617 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1617] filename</a>"]
        end
            v2 --> v3
            v3 --> v4
            v4 --> v5
            v5 --> v6
            v6 --> v7
            v7 --> v8
            v8 --> v9
            v9 --> v10
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/dotCMS/core/blob/4e93115a4430825a74060565d6e255ef2a6a418a/dotCMS/src/main/java/com/dotcms/rest/ContentResource.java#L1631 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 1631] new File(<br>                    tmpFolder.getAbsolutePath() + File.separator + filename)</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink

Loading

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 1 jsp-scriptlet-xss finding:

  • dotCMS/src/main/webapp/html/portlet/ext/contentlet/edit_contentlet_basic_properties.jsp

Detected request.getParameter(...) used directly inside a JSP expression scriptlet (<%= ... %>). This writes the raw, user-controlled value into the HTTP response without HTML encoding, enabling reflected Cross-Site Scripting (XSS). Wrap the value with an encoding function such as ESAPI.encoder().encodeForHTML(...), Encode.forHtml(...) (OWASP Java Encoder), or HtmlUtils.htmlEscape(...) (Spring) before output.

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

Semgrep found 3 hooks-dns-exfiltration-generic findings:

DNS lookup command uses a variable in the hostname, which could exfiltrate sensitive data via DNS queries. In Claude Code and Cursor hooks, this pattern can leak secrets or environment data to attacker-controlled domains. Use static hostnames only in DNS commands.

If this is a critical or high severity finding, please also link this issue in the #security channel in Slack.

@github-actions github-actions Bot added the Area : Backend PR changes Java/Maven backend code label Jun 19, 2026
@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @dsolistorres's task in 2m 23s —— View job


Claude finished @dsolistorres's task in 2m 35s —— View job


Rollback Safety Analysis

  • Read rollback-unsafe categories reference
  • Get full PR diff
  • Analyze diff against all categories
  • Post result and apply label

Result: ✅ Safe to Rollback

The label AI: Safe To Rollback has been applied.

Files changed: 3 (all Java — no SQL, no ES mapping, no runonce tasks)

  • PageRenderUtil.java — skips archived contentlets in populateContainers()
  • PageResource.javavalidateContainerEntries() skips archived on save; getContainerContentTypes() error handling hardened
  • PageResourceTest.java — integration test (test-only, no production impact)

Category-by-category analysis:

Category Verdict
C-1 Structural Data Model Change ✅ No schema change; no runonce task; multi_tree associations only change on next user-initiated page save
C-2 Elasticsearch Mapping Change ✅ No ES mapping code touched
C-3 Content JSON Model Version Bump CURRENT_MODEL_VERSION not touched
C-4 DROP TABLE / DROP COLUMN ✅ No DDL
H-1 One-Way Data Migration ✅ No data transformation; no UPDATE … SET across rows
H-2 RENAME TABLE / COLUMN ✅ None
H-3 PK Restructuring ✅ None
H-4 New ContentType Field Type ✅ None
H-5 Binary Storage Provider Change ✅ None
H-6 DROP PROCEDURE / FUNCTION ✅ None
H-7 NOT NULL column without default ✅ None
H-8 VTL Viewtool Contract Change PageRenderUtil is not a ViewTool; the REST response shape is unchanged — only the content of rendered containers differs (fewer items when archived). No accessor added or renamed on any object reachable from a VTL call.
M-1 Non-Broadening Column Type Change ✅ None
M-2 Push Publishing Bundle Format ✅ None
M-3 REST / GraphQL Contract Change ✅ The /api/v1/page/{id}/content (save) and /api/v1/page/render (load) response schema is unchanged. The behavioral change (archived content no longer in rendered output) is not a contract breakage — the JSON field set is identical, only the count of contentlets in containers differs. The error response for validateContainerEntries now uses a generic message instead of forwarding a raw exception message — this is a hardening, not a contract change. getContainerContentTypes now returns 403 instead of 400 for DotSecurityException; this is a bug-fix (wrong status code) rather than a contract regression.
M-4 OSGi Plugin API Breakage ✅ No public interface or OSGi service signature changed

Rollback behavior: Rolling back to N-1 simply restores the old behavior — archived content reappears in EDIT/PREVIEW renders and the "spinning forever" page-save bug returns. No data loss, no schema mismatch, no ES index corruption.

@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — deepseek.v3.2

[🟠 High] dotCMS/src/main/java/com/dotcms/rendering/velocity/services/PageRenderUtil.java:304 — The isArchived() method can throw DotSecurityException (as noted in the comment), but the catch block only handles DotStateException | DotDataException. If a DotSecurityException is thrown, it will propagate and potentially break page rendering for a legitimate access-control failure, which may be too severe. The comment says this is intentional, but the method signature doesn't declare DotSecurityException, and the caller (populateContainers) doesn't handle it either, risking an uncaught exception.

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1060 — The ExceptionUtil.causedBy(e, NotFoundInDbException.class) check may be too broad; NotFoundInDbException could be thrown for reasons other than archived/non-existent content (e.g., a corrupted DB state). Treating all such cases as benign and continuing silently could mask real data integrity issues. At minimum, log the exception cause for debugging.

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1078 — Catching DotDataException separately after DotContentletStateException is redundant, as the comment states findContentletByIdentifierAnyLanguageAnyVariant wraps failures in DotContentletStateException. This catch block may never be reached, creating dead code and potential confusion.

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1111 — Changing a DotSecurityException to a ForbiddenException changes the HTTP status code from 400 to 403. This could break existing client error handling that expects a 400 for permission issues in this context. Ensure this aligns with API contracts and existing tests.

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1124 — The comment states DoesNotExistException is intentionally not caught, but the method getContainerContentTypes catches DotDataException. DoesNotExistException extends DotDataException, so it would be caught here and incorrectly turned into a BadRequestException instead of being re-thrown as a DoesNotExistException. This violates the stated intent and could break the API contract verified by the postman test.


Run: #27851606331 · tokens: in: 3785 · out: 550 · total: 4335

@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🔍 dotCMS Backend Review

[🟠 High] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1033

validateContainerEntries now declares throws DotDataException, but both callers (addContent at line 1009 and updateStyles at line 1983) are wrapped in try-catches that only handle HTMLPageAssetNotFoundException. A DotDataException thrown here escapes those local try-blocks and is handled only by JAX-RS exception mappers, which will serialize it using DotDataException.getMessage() — potentially including SQL fragments or internal identifiers in the HTTP 400 response body. This is inconsistent with the intent elsewhere in the same method, which explicitly uses a generic BadRequestException message to avoid leaking internals.

// The method declares checked DotDataException...
protected void validateContainerEntries(...) throws DotDataException {

// ...but both callers catch only HTMLPageAssetNotFoundException
} catch(HTMLPageAssetNotFoundException e) { ... }
// DotDataException propagates to JAX-RS mapper, leaking raw exception message

💡 Remove throws DotDataException from the method signature — all exceptions thrown inside are unchecked (BadRequestException). The DotContentletStateException case is already caught internally. Both callers have throws DotDataException on their own signatures, so compilation is unaffected, but the declaration is misleading and creates the unintended leakage path.


[🟠 High] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1063

The warn log for the expected not-found case passes the full exception e with its cause chain: Logger.warn(this, "...", e). Because findContentletByIdentifierAnyLanguageAnyVariant wraps ALL exceptions in DotContentletStateException (see ESContentletAPIImpl.java:929), the cause of e in this code path is always a NotFoundInDbException — but that exception's message can still include the raw identifier and internal lookup path. For the expected/benign "archived or not found" case, logging the full exception at WARN level pollutes the log with a stack trace and cause message for every page save that references an archived contentlet.

Logger.warn(this, "Skipping contentlet '" + contentletId
        + "' on page content save (archived or not found)", e);
// ↑ Logs full stack trace + NotFoundInDbException message on every archived-content save

💡 For the expected case, log without the exception: Logger.warn(this, "Skipping contentlet '" + contentletId + "' on page content save: archived or not found"). Reserve the exception argument for the re-throw path.


[🟡 Medium] dotCMS/src/main/java/com/dotcms/rendering/velocity/services/PageRenderUtil.java:304

Contentlet.isArchived() (line 906) declares throws DotStateException, DotDataException, DotSecurityException, but the underlying VersionableAPI.isDeleted() only declares throws DotDataException, DotStateException. The PR correctly catches only DotStateException | DotDataException, letting DotSecurityException propagate. The in-code comment accurately explains the intent. However, the comment would benefit from noting that DotSecurityException is declared on isArchived() but not thrown via this call path — it was added for forward-compatibility. Informational; no code change required.


[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1098

getContainerContentTypes() (pre-existing, unmodified) throws new BadRequestException(e, e.getMessage()), where e.getMessage() can carry raw SQL or internal details from DotDataException/DotSecurityException. The new validateContainerEntries code calls this helper on every page-save validation, increasing the exposure surface. Pre-existing issue, strictly out of scope for this PR, but worth addressing in a follow-up.

} catch (DotDataException | DotSecurityException e) {
    throw new BadRequestException(e, e.getMessage());  // raw exception message in HTTP response
}

💡 Follow-up: throw new BadRequestException("Error retrieving content types for container: " + containerId) and Logger.error(this, ..., e) separately.


Next steps

  • 🟠 Fix locally and push — these need your judgment
  • 🟡 You can ask me to handle mechanical fixes inline: @claude fix <issue description> in PageResource.java
  • Every new push triggers a fresh review automatically

@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🔍 dotCMS Backend Review

[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1104

DoesNotExistException is still constructed with containerId embedded in its message, even though the new catch block immediately intercepts it and substitutes a generic BadRequestException. The identifier never reaches the HTTP response now, but the exception object carries it — if the catch block is ever narrowed, the DoesNotExistExceptionMapper will forward exception.getMessage() (including the id) to the caller. Removing the identifier from the constructor is a one-line defensive hardening.

.orElseThrow(() -> new DoesNotExistException("Container with ID :" + containerId + " not found"));

💡 "Container not found" — the id is already in the server-side Logger.error call below.


[🟡 Medium] dotcms-integration/src/test/java/com/dotcms/rest/api/v1/page/PageResourceTest.java:756

New test method testArchivedContentNotRenderedInEditAndPreviewMode instantiates Structure via StructureDataGen, which is the legacy representation of a Content Type. The Structure Javadoc explicitly states it must not be used in new code. The same test already imports and uses ContentTypeAPI / ContentType for webPageContent, making the mixed usage internally inconsistent.

final Structure structure = new StructureDataGen().nextPersisted();

💡 Replace with new ContentTypeDataGen().nextPersisted() (returns a ContentType) and update the ContainerDataGen.withStructure(...) call accordingly.


Next steps

  • 🟡 You can ask me to handle mechanical fixes inline: @claude fix <issue description> in <File.java>
  • Every new push triggers a fresh review automatically

View job run

@dsolistorres

Copy link
Copy Markdown
Member Author

Review findings addressed

All findings from the automated backend-review passes have been resolved across the latest commits. Summary:

🔴 Critical

  • validateContainerEntries no longer wraps DotSecurityException as a 400 — it propagates to DotSecurityExceptionMapper (403 + security audit log).

🟠 High

  • Exception messages are no longer forwarded to the client; full detail is logged server-side and a generic message is returned (no PII / SQL / identifiers).
  • PageRenderUtil archived-state check no longer swallows DotSecurityException — only DotStateException/DotDataException warn-and-skip.
  • Single contentlet lookup (was up to 3 DB calls); the DotContentletStateException cause is inspected so a real DB error is surfaced rather than silently treated as "archived/not found".
  • Removed the misleading throws DotDataException from validateContainerEntries (it created a raw-message leak path via the JAX-RS mapper); the checked exception is handled internally.

🟡 Medium

  • Added isSet guards for containerId and contentletId.
  • Archived/not-found are treated identically (silent skip) to avoid leaking content existence.
  • Benign "archived or not found" case logs message-only (no stack trace) on every save; the exception is kept only on the error path.
  • getContainerContentTypes returns a generic message and logs detail server-side — including the unchecked DoesNotExistException path that previously leaked containerId via the 404 mapper.
  • Test uses APILocator.getContentletAPI() (not the Impl) and reuses the systemUser local.

Verification: each round, dotcms-core builds and the full PageResourceTest suite passes 31/31 (build cache disabled). The regression test testArchivedContentNotRenderedInEditAndPreviewMode was confirmed to fail against the unfixed core and pass with the fix.

Reviewer note (unchanged decision): archived content is de-associated from a page on the next save because the page-content endpoint is a full replacement. If content is later unarchived it will not automatically reappear on the page. This is intentional — see the PR description.

@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🔍 dotCMS Backend Review

[🟠 High] dotcms-integration/src/test/java/com/dotcms/rest/api/v1/page/PageResourceTest.java:767

The new test method uses StructureDataGen / Structure — the legacy content-type representation listed under "Legacy Patterns to Avoid" in JAVA_STANDARDS.md. ContainerDataGen already has a withContentType(ContentType, String) overload for the modern API.

final Structure structure = new StructureDataGen().nextPersisted();
final Container localContainer = new ContainerDataGen().withStructure(structure, "")

💡 Replace with new ContentTypeDataGen().nextPersisted() and ContainerDataGen().withContentType(contentType, "").


[🟠 High] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1054

The archived/not-found branch relies on e.getCause() instanceof NotFoundInDbException, checking only the direct cause. If ESContentletAPIImpl ever adds an intermediate wrapper (e.g., new DotContentletStateException("msg", new SomeException(new NotFoundInDbException(...)))), getCause() won't match, the archived path silently turns into a BadRequestException, and legitimate page saves start failing without a compile-time guard.

if (e.getCause() instanceof NotFoundInDbException) {
    // continue (skip archived)
}

💡 Use ExceptionUtils.indexOfType(e, NotFoundInDbException.class) >= 0 (Apache Commons Lang) to check the full cause chain rather than only the direct cause.


[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1105

DotDataException and DotSecurityException are both collapsed into the same BadRequestException (400). A DotSecurityException from getContentTypesInContainer means the system user lacks permission to read the container — a server-side misconfiguration, not a client bad request. Mapping it 400 misdirects both callers and operators looking at logs.

} catch (DotDataException | DotSecurityException e) {
    throw new BadRequestException("Error retrieving content types for the container");
}

💡 Separate the catches: re-throw DotSecurityException as-is (the DotSecurityExceptionMapper maps it to 403) and keep only DotDataException mapped to BadRequestException.


[🟡 Medium] dotCMS/src/main/java/com/dotcms/rest/api/v1/page/PageResource.java:1094

The new generic validation message "The content type assigned to a contentlet is not valid for the container" deliberately avoids leaking internal identifiers — correct. However, the content-type variable (e.g., "webPageContent") is public schema metadata, not a sensitive internal value, and its absence makes it impossible for an API consumer to identify which contentlet's type is invalid without a separate lookup.

throw new BadRequestException("The content type assigned to a contentlet is not valid for the container");

💡 Including the content-type variable is safe: "Content type '" + contentlet.getContentType().variable() + "' is not allowed in this container". The variable is caller-visible schema metadata already exposed via the Content Types API.


Next steps

  • 🟠 Fix locally and push — these need your judgment
  • 🟡 You can ask me to handle mechanical fixes inline: @claude fix <issue description> in <File.java>
  • Every new push triggers a fresh review automatically

@dsolistorres

Copy link
Copy Markdown
Member Author

Thanks — addressing both observations:

🟡 StructureDataGenContentTypeDataGen (test): Applied in f24972fc99. The new test now builds the container via ContainerDataGen.withContentType(new ContentTypeDataGen()...) instead of the legacy Structure. Behavior is unchanged (rendering is driven by the MultiTree); the test still passes.

🟡 containerId in DoesNotExistException message — intentionally NOT changed. Heads-up for the next review pass so this isn't re-flagged:

  • I reverted catching DoesNotExistException in getContainerContentTypes (commit 412c5c1975). A missing container must surface as HTTP 404 with the message "Container with ID :<id> not found" — this is an established API contract asserted by the Define_Contentlets_StyleProperties postman collection ("Unexistd container error" test). Catching it (or removing the id from the message) turns the 404 into a 400 / changes the body and breaks that test.
  • The id in that message is the caller's own request input (e.g. fake-123abc-1), not sensitive data or an internal identifier — echoing it back in a not-found error is standard REST behavior, not an information leak.

The genuine leak vectors in this method — DotDataException / DotSecurityException (which can carry SQL fragments or other users' PII) — are still sanitized: full detail is logged server-side and a generic message is returned to the client. Only the caller-supplied container id in the 404 is intentionally preserved.

dsolistorres and others added 3 commits June 19, 2026 16:04
…#35993)

Archived contentlets kept their working version, so showLive=false lookups
in EDIT/PREVIEW mode still resolved and rendered them; the page-save endpoint
then returned a 400 ("Can't find contentlet") for the archived id in the
payload, hanging content removal ("spins forever").

- PageRenderUtil.populateContainers() now skips contentlet.isArchived() in all
  modes, consistent with LIVE-mode behavior.
- PageResource.validateContainerEntries() skips archived content instead of
  throwing the 400 that caused the hang, so a page referencing archived content
  stays saveable.
- Adds self-validating integration test (renders before archive -> 1, after -> 0).

Refs: #35993

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…35993)

Refines validateContainerEntries per PR review:
- Do not catch DotSecurityException: let it propagate to DotSecurityExceptionMapper
  (HTTP 403 + security audit log) instead of wrapping it as a 400.
- Do not leak exception messages (PII / SQL fragments) to the client: log full
  detail server-side and return a generic message.
- Single contentlet lookup instead of three (drop the includeDeleted lookup +
  isArchived() call); archived content is detected via the existing
  AnyLanguageAnyVariant lookup which excludes deleted versions.
- Treat "archived" and "not found" identically (silent skip) to avoid leaking
  content existence.
- Guard against blank/null contentletId before hitting the DB.
- Test: use APILocator.getContentletAPI() (not the Impl) and reuse the systemUser
  local instead of APILocator.systemUser().

Refs: #35993

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- PageRenderUtil: stop swallowing DotSecurityException in the archived-state
  check; only DotStateException/DotDataException warn-and-skip, DotSecurityException
  now propagates so access-control failures are not hidden.
- PageResource.validateContainerEntries:
  - findContentletByIdentifierAnyLanguageAnyVariant wraps ALL errors (incl. real DB
    failures) in DotContentletStateException; inspect the cause -- skip only when it
    is NotFoundInDbException (archived/not found), otherwise surface the error
    instead of silently dropping the contentlet.
  - return a generic client-facing message (no contentletId) and log full detail
    server-side with the cause chain (Logger.warn/error with the exception).
  - add an isSet guard for containerId.
  - declare throws DotDataException (removes the now-unreachable catch).

Refs: #35993

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dsolistorres and others added 4 commits June 19, 2026 16:04
- validateContainerEntries: drop "throws DotDataException" (it created a path for
  raw exception messages to reach the JAX-RS mapper). The declared checked
  DotDataException from the lookup is now caught internally and re-thrown as a
  generic BadRequestException, so the method only emits unchecked, sanitized errors.
- Log the expected "archived or not found" case without the exception (no stack
  trace / wrapped lookup message on every archived-content save); keep the exception
  only on the genuine-error path.
- getContainerContentTypes: return a generic client message and log full detail
  server-side instead of forwarding e.getMessage() (tightens a pre-existing leak in
  the validation call path).
- PageRenderUtil: clarify the comment on why DotSecurityException is left uncaught.

Refs: #35993

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getContainerContentTypes throws an unchecked DoesNotExistException whose message
embeds the containerId; it escaped the catch added previously and reached the
JAX-RS mapper, leaking the identifier in the 404 response. Include it in the
catch and return the generic message (full detail still logged server-side).

Refs: #35993

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#35993)

Reverts catching DoesNotExistException in getContainerContentTypes. Catching it
turned a missing container into a 400 with a generic message, breaking the
Define_Contentlets_StyleProperties postman test which asserts 404 + "Container
with ID :<id> not found". The id in that message is the caller's own input (not
sensitive), and the 404 + message is an established, tested API contract.
DotDataException/DotSecurityException are still sanitized.

Refs: #35993

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#35993)

Per review: new test code should use the modern ContentType API rather than the
legacy Structure. Switch the archived-content test's container to
ContainerDataGen.withContentType(ContentTypeDataGen...). Behavior is unchanged
(rendering is driven by the MultiTree).

Refs: #35993

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dsolistorres dsolistorres force-pushed the issue-35993-archived-content-render branch from f24972f to e6ce86b Compare June 19, 2026 22:04
- validateContainerEntries: detect the archived/not-found case with
  ExceptionUtil.causedBy(e, NotFoundInDbException.class) (full cause chain) instead
  of checking only the direct getCause(), so an added intermediate wrapper can't
  silently turn it into a client error.
- getContainerContentTypes: split the catch so DotSecurityException is re-thrown as
  ForbiddenException (403 via the mapper) rather than collapsed into a 400; a system
  user lacking container read permission is a server misconfiguration, not a bad
  request. DotDataException still maps to a generic 400; DoesNotExistException still
  surfaces the 404 contract.
- Include the (public, non-sensitive) content-type variable in the
  "not allowed in this container" message for a more actionable client error.

Refs: #35993

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dsolistorres

Copy link
Copy Markdown
Member Author

Review status — addressed vs. intentionally rejected

Pushed 3da7152550. Summary of the latest review items:

✅ Addressed

  • 🟠 StructureDataGen in the new test → switched to ContentTypeDataGen + ContainerDataGen.withContentType(...) (f24972fc99).
  • 🟠 NotFoundInDbException matched only on the direct cause → now uses ExceptionUtil.causedBy(e, NotFoundInDbException.class), which walks the full cause chain, so an added intermediate wrapper can't silently turn the archived/not-found path into a client error.
  • 🟡 DotSecurityException collapsed into a 400 in getContainerContentTypes → split the catch: DotSecurityException is re-thrown as ForbiddenException (→ 403 via the mapper, generic message), DotDataException stays a generic 400.
  • 🟡 Content-type validation message too generic → now includes the content-type variable ("Content type '<var>' is not allowed in this container"); the variable is public schema metadata, not sensitive.

🚫 Intentionally not changed (reiterating, with rationale)

  • 🟡 containerId embedded in DoesNotExistException message — a missing container must surface as HTTP 404 with "Container with ID :<id> not found". This is a tested API contract (Define_Contentlets_StyleProperties → "Unexistd container error"); changing it (or removing the id) breaks that postman test. The id is the caller's own request input, not sensitive data. The genuine leak vectors (DotDataException / DotSecurityException) are sanitized.

Verification: dotcms-core builds, full PageResourceTest passes 31/31 (cache disabled), and the postman regression that broke earlier is restored by keeping the 404 contract.

@dsilvam dsilvam added this pull request to the merge queue Jun 20, 2026
Any commits made after this event will not be merged.
@mergify

mergify Bot commented Jun 20, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Archived content still renders in page Edit/Preview mode when used in a container

2 participants