Summary
notebookjs v0.8.3 defaults to an identity (no-op) HTML sanitizer when DOMPurify is not pre-loaded by the host application. The browser build (notebook.min.js) does not bundle DOMPurify. Any application rendering untrusted Jupyter notebooks in a browser without separately loading DOMPurify is vulnerable to Stored Cross-Site Scripting (XSS).
Root Cause
notebook.js line 67:
sanitizer: getSanitizer() || ident
getSanitizer() returns undefined when DOMPurify is not present as a global variable in the browser. The fallback function ident returns its input unchanged. All HTML output passes through nb.sanitizer(joinText(html)) — which becomes a no-op, directly setting el.innerHTML to attacker-controlled notebook content.
The README documents DOMPurify as optional:
"The browser-based version does not ship with those libraries, so you must <script>-include or require them before initializing notebook.js."
This creates a security vulnerability in any integration where the developer either:
- Omits the DOMPurify script tag
- Is unaware that DOMPurify is required for security (the README frames it as a Markdown/ANSI dependency, not a security requirement)
Steps to Reproduce
- Install notebookjs:
npm install notebookjs
- Serve
notebook.min.js in a browser page
- Render this
.ipynb file:
{"cells":[{"cell_type":"markdown","metadata":{},"source":["<img src=x onerror=alert('NPM-NOTEBOOKJS-XSS')>"]}],"metadata":{},"nbformat":4,"nbformat_minor":2}
- The
onerror handler executes — stored XSS confirmed.
PoC Files
<!DOCTYPE html>
<html><body><div id="output"></div>
<script src="notebook.min.js"></script>
<script>
fetch('xss.ipynb').then(r => r.json()).then(ipynb => {
document.getElementById('output').appendChild(nb.parse(ipynb).render());
});
</script>
</body></html>
Impact
An attacker uploads a malicious .ipynb file containing an XSS payload to any platform that renders notebooks using notebookjs without DOMPurify. When a victim views the notebook, the payload executes in their browser session — enabling session hijacking, credential theft, and full account takeover.
Affected Versions
All versions through 0.8.3 (latest). All known forks (@openbayes/notebookjs, @determined-ai/notebook-render) are also affected.
Fix
Bundle dompurify with the browser build so the sanitizer is always functional. At minimum, add a console warning when the sanitizer falls back to ident:
if (!lib) {
console.warn('notebookjs: DOMPurify not loaded — HTML output will not be sanitized. XSS risk.');
}
return lib ? lib.sanitize : function(x) { return x; };
Or throw an error if DOMPurify is missing, forcing integrators to include it.
Affected package
notebookjs (npm) — all versions ≤ 0.8.3
Summary
notebookjs v0.8.3 defaults to an identity (no-op) HTML sanitizer when DOMPurify is not pre-loaded by the host application. The browser build (
notebook.min.js) does not bundle DOMPurify. Any application rendering untrusted Jupyter notebooks in a browser without separately loading DOMPurify is vulnerable to Stored Cross-Site Scripting (XSS).Root Cause
notebook.jsline 67:getSanitizer()returnsundefinedwhen DOMPurify is not present as a global variable in the browser. The fallback functionidentreturns its input unchanged. All HTML output passes throughnb.sanitizer(joinText(html))— which becomes a no-op, directly settingel.innerHTMLto attacker-controlled notebook content.The README documents DOMPurify as optional:
This creates a security vulnerability in any integration where the developer either:
Steps to Reproduce
npm install notebookjsnotebook.min.jsin a browser page.ipynbfile:{"cells":[{"cell_type":"markdown","metadata":{},"source":["<img src=x onerror=alert('NPM-NOTEBOOKJS-XSS')>"]}],"metadata":{},"nbformat":4,"nbformat_minor":2}onerrorhandler executes — stored XSS confirmed.PoC Files
Impact
An attacker uploads a malicious
.ipynbfile containing an XSS payload to any platform that renders notebooks using notebookjs without DOMPurify. When a victim views the notebook, the payload executes in their browser session — enabling session hijacking, credential theft, and full account takeover.Affected Versions
All versions through 0.8.3 (latest). All known forks (
@openbayes/notebookjs,@determined-ai/notebook-render) are also affected.Fix
Bundle
dompurifywith the browser build so the sanitizer is always functional. At minimum, add a console warning when the sanitizer falls back toident:Or throw an error if DOMPurify is missing, forcing integrators to include it.
Affected package
notebookjs(npm) — all versions ≤ 0.8.3