Files
anonymous_github/public/script/external/marked-mermaid.js
T
Thomas Durieux e4ffd74068 Security hardening + gist UI fixes (#731)
* security: harden against XSS, ReDoS, path traversal, and injection

Defensive fixes across the server, storage, and viewer:

- XSS (CWE-79): sanitise rendered notebooks with DOMPurify, escape file
  names interpolated into AngularJS expressions (escapeNgString), set
  Mermaid securityLevel to 'strict', and stop urlRel2abs from returning
  javascript:/vbscript:/data:text/html URLs.
- Path traversal / zip-slip (CWE-22/23/24): validate URL-derived path
  components before they reach the storage layer (file/webview routes +
  StorageBase.assertSafePath) and sanitise zip entry names on extract for
  both the filesystem and S3 backends.
- ReDoS (CWE-1333): escape anonymization terms with catastrophic
  backtracking shapes to literals instead of compiling them as regexes.
- Secret hardening (CWE-798): require SESSION_SECRET / OAuth creds / DB
  password in production, random dev SESSION_SECRET fallback.
- Rate-limit spoofing (CWE-290): derive request.ip via trust-proxy hop
  count instead of the client-settable cf-connecting-ip header.
- NoSQL injection (CWE-943): allow only plain field paths as admin sort keys.
- Reject malformed streamer requests missing required string fields.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(ui): make gists reachable/visible and clarify the ZIP button

- Gist & PR routes now accept a trailing slash (/gist/:id/:path*?), so the
  dashboard links (which end in "/") resolve to the gist/PR page instead of
  falling through to the 404 route (#725).
- Gist viewer picks the default tab after content loads, defaulting to
  "files" when files exist; previously the ng-init ran before the async
  load and a files-only gist rendered blank under the hidden comments tab.
- Explorer toolbar: relabel ZIP to "Full repo ZIP" with a tooltip, and add
  tooltips to Raw/Download clarifying they apply to the current file (#721).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: report SAML-enforced orgs clearly instead of "token expired"

When a repo's organization enforces SAML SSO, GitHub returns a 403 whose
message differs from the OAuth-App-restriction case. That 403 fell through
to the generic handler and surfaced as "token_expired", pushing users to
re-login when the real fix is authorizing their token for the org. Detect
the "SAML enforcement" message and raise a dedicated, actionable error
instead (#379, #550).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* security: catch nested quantified groups in ReDoS guard and backslash path traversal

- hasCatastrophicBacktracking now scans across nested parens ([\s\S]*?)
  so shapes like ((a+))+ are detected; comment reframed as a heuristic
  backstop rather than a proof.
- file route path-traversal check now rejects backslash separators and a
  leading backslash, covering Windows-style "..\" payloads (CWE-22/25).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(dev): track dev-proxy script, ignore .DS_Store and .claude/

scripts/dev-proxy.js is referenced by the "dev:ui" npm script but was
never committed, breaking the command on a fresh clone. Add it and
ignore local-only macOS/Claude Code files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 13:50:55 +02:00

77 lines
2.4 KiB
JavaScript

// Marked.js extension for Mermaid diagrams
function markedMermaid(options) {
options = options || {};
return {
extensions: [
{
name: 'mermaid',
level: 'block',
start(src) {
return src.match(/^```mermaid/m)?.index;
},
tokenizer(src, tokens) {
const rule = /^```mermaid\n([\s\S]*?)\n```/;
const match = rule.exec(src);
if (match) {
return {
type: 'mermaid',
raw: match[0],
text: match[1].trim()
};
}
},
renderer(token) {
const id = 'mermaid-' + Math.random().toString(36).substr(2, 9);
// Create a div that will be processed by Mermaid
const div = `<div class="mermaid" id="${id}">${token.text}</div>`;
// Trigger lazy-load of mermaid if not yet loaded
if (typeof mermaid === 'undefined' && typeof window.loadMermaid === 'function') {
window.loadMermaid();
}
// Schedule Mermaid rendering for after DOM insertion
setTimeout(() => {
if (typeof mermaid === 'undefined') return;
if (!window.mermaidInitialized) {
mermaid.initialize({
startOnLoad: false,
theme: 'default',
// 'strict' keeps Mermaid's own HTML/script sanitisation and
// disables click-binding callbacks. 'loose' (the previous
// value) lets diagram syntax inject clickable elements with
// JavaScript handlers that run in the viewer's browser
// (XSS, CWE-79).
securityLevel: 'strict'
});
window.mermaidInitialized = true;
}
try {
const element = document.getElementById(id);
if (element && !element.getAttribute('data-processed')) {
mermaid.init(undefined, element);
element.setAttribute('data-processed', 'true');
}
} catch (error) {
console.error('Mermaid rendering error:', error);
}
}, 100);
return div;
}
}
]
};
}
// Make it available globally
if (typeof window !== 'undefined') {
window.markedMermaid = markedMermaid;
}
// CommonJS/Node.js export
if (typeof module !== 'undefined' && module.exports) {
module.exports = markedMermaid;
}