T09 · Insecure Skill Coding Practices
- Location
assets/server.js:31- Finding
Unauthenticated Cross-Origin Disk Writes Enable Persistent PRD Poisoning and Stored Script Execution
- Content
View full analysis
Vulnerability Details
File Location:
assets/server.js:31-37, 46-87;assets/prd-pin-tool.js:3788-3855, 4624-4647
Vulnerability Type: Unauthenticated cross-origin file write chained with unsafe Markdown link rendering
Risk Level: HighTechnical Analysis
The local persistence server permits requests from every web origin and exposes its write endpoint without authentication.
Relevant code from
assets/server.js:31-37:javascript // 允许跨域 (CORS) res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }The
/api/save-prdendpoint accepts attacker-supplied JSON and writes itsdatafield into a JavaScript file without authenticating the caller.Relevant code from
assets/server.js:46-87:javascript if (pathname === '/api/save-prd' && req.method === 'POST') { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { try { const payload = JSON.parse(body); const { page, data, allData } = payload; if (!page) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, error: '缺少 page 标识' })); return; } const jsDir = path.join(BASE_DIR, 'assets', 'js'); if (!fs.existsSync(jsDir)) { fs.mkdirSync(jsDir, { recursive: true }); } // 1.1 写入页面专属数据文件 prd-data-[page].js const cleanPageName = page.replace('.html', '').replace(/^prd-data-/, ''); const pageFileName = `prd-data-${cleanPageName}.js`; const pageFilePath = path.join(jsDir, pageFileName); const pageJsContent = `/** * PRD 需求数据 - ${cleanPageName} * 本地实时保存于: ${new Date().toLocaleString()} */ window.INITIAL_PRD_DATA = ${JSON.stringify(data || [], null, 2)}; `; fs.writeFileS ...[truncated 5568 chars]- Remediation
View remediation
Remediation Suggestions
-
Bind the development server to loopback explicitly
javascript server.listen(PORT, '127.0.0.1', () => { // startup logging });This reduces exposure to other devices, although it does not by itself prevent browser-to-localhost attacks.
-
Require authentication for every write operation
- Generate a cryptographically random token when the server starts.
- Require the token in an authorization header for
/api/save-prd. - Compare tokens using a timing-safe comparison.
- Do not expose the token through unauthenticated static files or logs accessible to other users.
-
Replace wildcard CORS with an exact allowlist
- Permit only explicitly configured prototype origins.
- Reject unexpected, absent, and
nullorigins for browser-facing write requests. - Return
Vary: Origin. - Do not reflect arbitrary origins.
- Treat CORS as defense in depth rather than a replacement for authentication.
-
Validate and constrain the
pagevalueApply a strict allowlist, for example:
javascript if (!/^[A-Za-z0-9_-]+(?:\.html)?$/.test(page)) { // reject request }Resolve the final path and verify it remains under the intended
assets/jsdirectory before writing. -
Limit request size
Stop buffering and return an error once the request exceeds an appropriate maximum size. This also prevents unauthenticated memory exhaustion.
-
Use safe Markdown rendering
- Construct links using DOM APIs.
- Set link labels with
textContent. - Assign validated URLs through element properties rather than string interpolation.
- Permit only expected schemes such as
https:,http:, and approved relative links. - Reject
javascript:,data:,vbscript:, and malformed schemes.
-
Sanitize generated markup before insertion
If HTML rendering remains necessary, process it with a maintained sanitizer configured to remove:
...[truncated 749 chars]
-
