diff --git a/front/assets/js/report/index.tsx b/front/assets/js/report/index.tsx
index ed07c5702..02f71bc12 100644
--- a/front/assets/js/report/index.tsx
+++ b/front/assets/js/report/index.tsx
@@ -9,7 +9,7 @@ import DOMPurify from 'dompurify';
import * as toolbox from "js/toolbox";
import { useEffect, useState } from "preact/hooks";
-Mermaid.initialize({ startOnLoad: false, theme: `default`, securityLevel: `strict` });
+Mermaid.initialize({ startOnLoad: false, theme: `default`, securityLevel: `sandbox` });
const md = MarkdownIt({
html: true,
linkify: false,
@@ -83,6 +83,21 @@ const MarkdownBody = (props: { markdown: string, }) => {
}, [props.markdown]);
const renderedHtml = md.render(props.markdown);
+ // Note: Sanitization is applied here before mermaid rendering. Some mermaid-specific tags
+ // (like those generated by markdown-it-textual-uml) are intentionally not included in ALLOWED_TAGS
+ // because they're processed by Mermaid.run() after sanitization.
+ // Additionally, Mermaid provides two more security layers:
+ // 1. It uses DOMPurify internally for sanitizing diagram content
+ // 2. With securityLevel: 'sandbox' configured above, it renders each diagram in a sandboxed iframe
+ // First, configure DOMPurify hooks
+ DOMPurify.addHook(`afterSanitizeAttributes`, function(node) {
+ // Force all links to open in new tab with security attributes
+ if (`tagName` in node && node.tagName === `A`) {
+ node.setAttribute(`target`, `_blank`);
+ node.setAttribute(`rel`, `noopener noreferrer nofollow`);
+ }
+ });
+
const sanitizedHtml = DOMPurify.sanitize(renderedHtml, {
ALLOWED_TAGS: [
// Basic
@@ -98,17 +113,29 @@ const MarkdownBody = (props: { markdown: string, }) => {
`mark`,
`ins`,
`small`,
- `abbr`
+ `abbr`,
+ // Links (safe with DOMPurify's URL sanitization)
+ `a`
],
ALLOWED_ATTR: [
`title`,
- `open`
+ `open`,
+ `class`,
+ `href`, // DOMPurify by default blocks dangerous protocols (javascript:, data:, vbscript:) and only allows safe ones (http:, https:, mailto:, etc.)
+ `target`,
+ `rel`
],
- FORBID_TAGS: [`a`, `img`, `script`, `object`, `embed`, `iframe`, `link`],
- FORBID_ATTR: [`href`, `src`, `class`, `id`, `style`, `target`],
- ALLOW_DATA_ATTR: false
+ // Critical: Keep blocking dangerous tags that were part of the original vulnerability
+ FORBID_TAGS: [`img`, `script`, `object`, `embed`, `iframe`, `link`, `form`, `input`, `style`, `meta`, `base`],
+ FORBID_ATTR: [`src`, `id`, `style`, `onclick`, `onload`, `onerror`, `action`, `method`],
+ ALLOW_DATA_ATTR: false,
+ // Force secure link attributes
+ ADD_ATTR: [`target`, `rel`]
});
+ // Clean up the hook after sanitization to avoid memory leaks
+ DOMPurify.removeHook(`afterSanitizeAttributes`);
+
return (
=14"
@@ -9650,13 +9661,13 @@
}
},
"node_modules/mermaid": {
- "version": "11.6.0",
- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz",
- "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==",
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.0.tgz",
+ "integrity": "sha512-ZudVx73BwrMJfCFmSSJT84y6u5brEoV8DOItdHomNLz32uBjNrelm7mg95X7g+C6UoQH/W6mBLGDEDv73JdxBg==",
"dependencies": {
- "@braintree/sanitize-url": "^7.0.4",
- "@iconify/utils": "^2.1.33",
- "@mermaid-js/parser": "^0.4.0",
+ "@braintree/sanitize-url": "^7.1.1",
+ "@iconify/utils": "^3.0.1",
+ "@mermaid-js/parser": "^0.6.2",
"@types/d3": "^7.4.3",
"cytoscape": "^3.29.3",
"cytoscape-cose-bilkent": "^4.1.0",
@@ -9664,12 +9675,12 @@
"d3": "^7.9.0",
"d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.11",
- "dayjs": "^1.11.13",
- "dompurify": "^3.2.4",
- "katex": "^0.16.9",
+ "dayjs": "^1.11.18",
+ "dompurify": "^3.2.5",
+ "katex": "^0.16.22",
"khroma": "^2.1.0",
"lodash-es": "^4.17.21",
- "marked": "^15.0.7",
+ "marked": "^16.2.1",
"roughjs": "^4.6.6",
"stylis": "^4.3.6",
"ts-dedent": "^2.2.0",
@@ -9677,14 +9688,14 @@
}
},
"node_modules/mermaid/node_modules/marked": {
- "version": "15.0.8",
- "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.8.tgz",
- "integrity": "sha512-rli4l2LyZqpQuRve5C0rkn6pj3hT8EWPC+zkAxFTAJLxRbENfTAhEQq9itrmf1Y81QtAX5D/MYlGlIomNgj9lA==",
+ "version": "16.3.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-16.3.0.tgz",
+ "integrity": "sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w==",
"bin": {
"marked": "bin/marked.js"
},
"engines": {
- "node": ">= 18"
+ "node": ">= 20"
}
},
"node_modules/mermaid/node_modules/uuid": {
@@ -10206,20 +10217,20 @@
}
},
"node_modules/mlly": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz",
- "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
+ "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
"dependencies": {
- "acorn": "^8.14.0",
- "pathe": "^2.0.1",
- "pkg-types": "^1.3.0",
- "ufo": "^1.5.4"
+ "acorn": "^8.15.0",
+ "pathe": "^2.0.3",
+ "pkg-types": "^1.3.1",
+ "ufo": "^1.6.1"
}
},
"node_modules/mlly/node_modules/acorn": {
- "version": "8.14.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
- "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"bin": {
"acorn": "bin/acorn"
},
@@ -11181,12 +11192,9 @@
}
},
"node_modules/package-manager-detector": {
- "version": "0.2.11",
- "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz",
- "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==",
- "dependencies": {
- "quansync": "^0.2.7"
- }
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz",
+ "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ=="
},
"node_modules/pako": {
"version": "2.1.0",
@@ -11340,12 +11348,12 @@
}
},
"node_modules/pkg-types": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz",
- "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
+ "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
"dependencies": {
- "confbox": "^0.2.1",
- "exsolve": "^1.0.1",
+ "confbox": "^0.2.2",
+ "exsolve": "^1.0.7",
"pathe": "^2.0.3"
}
},
@@ -12118,11 +12126,11 @@
}
},
"node_modules/posthog-js": {
- "version": "1.261.6",
- "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.261.6.tgz",
- "integrity": "sha512-tson+4i+T2YkGYlj/oGjFwKRpBFqhM7Xr9ZmXGEtNFkZc6ZQHYCzObeeHT6BbKc5d/dAfMCPtvPCKssARaK6eQ==",
+ "version": "1.268.8",
+ "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.268.8.tgz",
+ "integrity": "sha512-BJiKK4MlUvs7ybnQcy1KkwAz+SZkE/wRLotetIoank5kbqZs8FLbeyozFvmmgx4aoMmaVymYBSmYphYjYQeidw==",
"dependencies": {
- "@posthog/core": "1.0.2",
+ "@posthog/core": "1.2.2",
"core-js": "^3.38.1",
"fflate": "^0.4.8",
"preact": "^10.19.3",
@@ -12315,9 +12323,9 @@
}
},
"node_modules/quansync": {
- "version": "0.2.10",
- "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz",
- "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
+ "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
"funding": [
{
"type": "individual",
@@ -13252,9 +13260,9 @@
"dev": true
},
"node_modules/tinyexec": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
- "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz",
+ "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw=="
},
"node_modules/tinyglobby": {
"version": "0.2.14",
diff --git a/front/assets/package.json b/front/assets/package.json
index 10a42a737..ef99bf15f 100644
--- a/front/assets/package.json
+++ b/front/assets/package.json
@@ -43,7 +43,7 @@
"markdown-it": "^14.1.0",
"markdown-it-textual-uml": "^0.17.1",
"marked": "^9.1.2",
- "mermaid": "^11.6.0",
+ "mermaid": "^11.12.0",
"moment": "^2.29.4",
"moment-duration-format": "^2.3.2",
"monaco-yaml": "^5.2.3",
@@ -68,6 +68,7 @@
"@types/chai": "^4.3.1",
"@types/d3": "^7.4.0",
"@types/d3-time-format": "^4.0.0",
+ "@types/dompurify": "^3.0.5",
"@types/jquery": "^3.5.14",
"@types/jsdom": "^21.1.5",
"@types/lodash": "^4.14.182",
diff --git a/front/priv/storage/reports/job_report.md b/front/priv/storage/reports/job_report.md
index cb7950d64..d23b8f151 100644
--- a/front/priv/storage/reports/job_report.md
+++ b/front/priv/storage/reports/job_report.md
@@ -1,21 +1,46 @@
-# Job Report
+## 🎯 System Metrics Summary
-This is a test job report.
+**Total datapoints:** `40`
+**🕒 Time Range:** `Tue Sep 23 08:57:29 UTC 2025` → `Tue Sep 23 08:58:10 UTC 2025`
+
+- **🔥 CPU:** `min: 3.40%`, `max: 135.80%`
+- **🧠Memory:** `min: 3.94%`, `max: 5.76%`
+- **💽 System Disk:** `min: 31.60%`, `max: 37.09%`
+- **🳠Docker Disk:** `min: 27.93%`, `max: 27.93%`
---
```mermaid
-gitGraph:
- commit "Ashish"
- branch newbranch
- checkout newbranch
- commit id:"1111"
- commit tag:"tessst"
- checkout main
- commit type: HIGHLIGHT
- commit
- merge newbranch
- commit
- branch b2
- commit
+xychart-beta
+title "CPU Usage"
+x-axis ["00:00", "00:01", "00:02", "00:04", "00:05", "00:06", "00:07", "00:08", "00:09", "00:10", "00:11", "00:12", "00:13", "00:14", "00:15", "00:16", "00:17", "00:18", "00:19", "00:20", "00:21", "00:22", "00:23", "00:24", "00:25", "00:26", "00:27", "00:29", "00:30", "00:31", "00:32", "00:33", "00:34", "00:35", "00:36", "00:37", "00:38", "00:39", "00:40", "00:41"]
+y-axis "Usage (%)"
+line [6.90, 12.00, 8.40, 25.10, 39.50, 44.30, 46.20, 47.90, 48.30, 43.50, 44.50, 46.40, 47.30, 47.90, 47.90, 10.20, 66.70, 3.70, 129.70, 135.80, 131.70, 129.70, 130.70, 129.70, 126.50, 122.40, 124.40, 126.50, 127.40, 128.40, 129.30, 130.30, 122.40, 123.30, 3.40, 3.40, 35.40, 23.40, 23.90, 3.40]
+bar [6.90, 12.00, 8.40, 25.10, 39.50, 44.30, 46.20, 47.90, 48.30, 43.50, 44.50, 46.40, 47.30, 47.90, 47.90, 10.20, 66.70, 3.70, 129.70, 135.80, 131.70, 129.70, 130.70, 129.70, 126.50, 122.40, 124.40, 126.50, 127.40, 128.40, 129.30, 130.30, 122.40, 123.30, 3.40, 3.40, 35.40, 23.40, 23.90, 3.40]
+```
+
+```mermaid
+xychart-beta
+title "Memory Usage"
+x-axis ["00:00", "00:01", "00:02", "00:04", "00:05", "00:06", "00:07", "00:08", "00:09", "00:10", "00:11", "00:12", "00:13", "00:14", "00:15", "00:16", "00:17", "00:18", "00:19", "00:20", "00:21", "00:22", "00:23", "00:24", "00:25", "00:26", "00:27", "00:29", "00:30", "00:31", "00:32", "00:33", "00:34", "00:35", "00:36", "00:37", "00:38", "00:39", "00:40", "00:41"]
+y-axis "Usage (%)"
+line [4.35, 4.36, 4.35, 4.34, 4.06, 4.22, 4.20, 4.21, 4.39, 4.72, 4.62, 4.75, 4.76, 4.65, 4.58, 5.76, 5.46, 5.35, 4.52, 4.51, 3.98, 4.08, 4.21, 3.96, 3.94, 4.11, 4.23, 4.07, 4.44, 4.15, 4.29, 4.05, 4.37, 4.57, 4.24, 4.72, 4.83, 4.81, 4.99, 4.84]
+bar [4.35, 4.36, 4.35, 4.34, 4.06, 4.22, 4.20, 4.21, 4.39, 4.72, 4.62, 4.75, 4.76, 4.65, 4.58, 5.76, 5.46, 5.35, 4.52, 4.51, 3.98, 4.08, 4.21, 3.96, 3.94, 4.11, 4.23, 4.07, 4.44, 4.15, 4.29, 4.05, 4.37, 4.57, 4.24, 4.72, 4.83, 4.81, 4.99, 4.84]
```
+
+```mermaid
+xychart-beta
+title "System Disk Usage"
+x-axis ["00:00", "00:01", "00:02", "00:04", "00:05", "00:06", "00:07", "00:08", "00:09", "00:10", "00:11", "00:12", "00:13", "00:14", "00:15", "00:16", "00:17", "00:18", "00:19", "00:20", "00:21", "00:22", "00:23", "00:24", "00:25", "00:26", "00:27", "00:29", "00:30", "00:31", "00:32", "00:33", "00:34", "00:35", "00:36", "00:37", "00:38", "00:39", "00:40", "00:41"]
+y-axis "Disk Usage (%)"
+line [31.60, 31.60, 31.60, 31.61, 31.65, 31.69, 31.73, 31.77, 31.81, 31.85, 31.89, 31.93, 31.97, 32.01, 32.05, 32.43, 32.68, 32.68, 32.86, 33.09, 33.24, 33.31, 33.65, 34.07, 33.70, 33.92, 34.44, 34.44, 34.73, 35.18, 35.68, 35.73, 36.05, 36.48, 36.78, 37.04, 37.05, 37.05, 37.05, 37.09]
+bar [31.60, 31.60, 31.60, 31.61, 31.65, 31.69, 31.73, 31.77, 31.81, 31.85, 31.89, 31.93, 31.97, 32.01, 32.05, 32.43, 32.68, 32.68, 32.86, 33.09, 33.24, 33.31, 33.65, 34.07, 33.70, 33.92, 34.44, 34.44, 34.73, 35.18, 35.68, 35.73, 36.05, 36.48, 36.78, 37.04, 37.05, 37.05, 37.05, 37.09]
+```
+```mermaid
+xychart-beta
+title "Docker Disk Usage"
+x-axis ["00:00", "00:01", "00:02", "00:04", "00:05", "00:06", "00:07", "00:08", "00:09", "00:10", "00:11", "00:12", "00:13", "00:14", "00:15", "00:16", "00:17", "00:18", "00:19", "00:20", "00:21", "00:22", "00:23", "00:24", "00:25", "00:26", "00:27", "00:29", "00:30", "00:31", "00:32", "00:33", "00:34", "00:35", "00:36", "00:37", "00:38", "00:39", "00:40", "00:41"]
+y-axis "Disk Usage (%)"
+line [27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93]
+bar [27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93, 27.93]
+```
\ No newline at end of file
diff --git a/front/priv/storage/reports/wf_report.md b/front/priv/storage/reports/wf_report.md
index e72b25695..af2ac2816 100644
--- a/front/priv/storage/reports/wf_report.md
+++ b/front/priv/storage/reports/wf_report.md
@@ -1,3 +1,179 @@
-# Workflow Report
+# Security requirements
+This markdown should be safely rendered to keep compliance with [project-tasks/issues#2650](https://github.com/renderedtext/project-tasks/issues/2650)
-This is a test workflow report.
+# Domain stats
+
+## Security Test - Safe Links and Potential XSS Attempts (for testing sanitization)
+
+These are safe test cases to verify sanitization is working:
+
+1. [Normal link to GitHub](https://github.com)
+2. [Link with onclick attempt](https://example.com" onclick="alert('XSS'))
+3.
JavaScript protocol test
+4.
Data URL test
+5. [Regular markdown link](https://www.example.com)
+6.
Link with onclick attribute
+7.
VBScript protocol test
+
+## Test - Mermaid Injection Attempts
+
+### Attempt 1: Breaking out with closing tags
+```mermaid
+graph TD
+ A[Start] --> B[Process]
+ B --> C[End