T09 · Insecure Skill Coding Practices
- Location
scripts/publish.js:96- Finding
Public post publication is not technically gated by user confirmation
- Content
View full analysis
Vulnerability Details
File Location:
scripts/publish.js, lines 96–166
Vulnerability Type: Missing executable authorization gate for a public publishing operation
Risk Level: HighComplete Code Snippet
js async function main() { const [, , htmlPath] = process.argv; if (!htmlPath) { console.error('Usage: node publish.js <html-file-path>'); process.exit(1); } let content = fs.readFileSync(htmlPath, 'utf-8'); console.log(`Content length: ${content.length} chars`); let session; try { session = requireSession(); } catch (err) { if (err instanceof SessionExpiredError) { console.error(`❌ ${err.message}`); process.exitCode = 2; return; } throw err; } console.log(`Publishing as: ${session.username}`); // Deferred local-image upload: this is the ONLY moment the images // leave the user's machine (compose.js keeps everything local). try { content = await uploadLocalImages(content, htmlPath); } catch (err) { if (err instanceof SessionExpiredError) { console.error(`❌ ${err.message}`); process.exitCode = 2; return; } console.error(`\n❌ Image upload failed: ${err.message}`); console.error(' Nothing was posted. Fix the issue and re-run publish.js ' + '(already-uploaded images are reused, not re-uploaded).'); process.exitCode = 1; return; } const dhInfo = extractDigitalHuman(content); if (dhInfo) console.log(`Digital Human: ${dhInfo.name} | ID: ${dhInfo.id}`); const locInfo = extractLocation(content); if (locInfo) console.log(`Location: ${locInfo.name} (${locInfo.lng}, ${locInfo.lat})`); // Guard: interactive components must use the editor's exact HTML structure, // otherwise the web frontend renders them as plain text. const formatIssues = validateEditorFormat(content); if (formatIssues.length > 0) { console.error('\n❌ Component HTML does not match the platform editor format:'); formatIssues.forEac ...[truncated 2724 chars]- Remediation
View remediation
Remediation Suggestions
- Make the default execution mode a dry run that displays the exact account, content hash, images, and location.
- After preview approval, issue a short-lived confirmation token bound to:
- The authenticated account.
- A cryptographic hash of the final draft.
- The image manifest and resolved image paths.
- The public-map location and exposure setting.
- Require and validate that token immediately before the first image upload and again before
POST /api/posts. - Invalidate the token after one use, after expiry, or whenever the draft or manifest changes.
- For interactive use, add a final explicit prompt immediately before external side effects.
- Keep the existing HTML validation, but do not treat it as authorization evidence.
