T09 · Insecure Skill Coding Practices
- Location
- scripts/publish.js:127
- Finding
- Public post publication and local image upload lack an executable confirmation gate## Vulnerability Details **File Location**: `scripts/publish.js`, lines 127–173 **Vulnerability Type**: Missing authorization confirmation for public publication and file upload **Risk Level**: High ### Complete Code Snippet ```js // 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.forEach(i => console.error(` - ${i}`)); console.error(' Fix: build the post with html-formatter.js helpers (formatPostHTML /'); console.error(' createDigitalHumanSpan / createLocationSpan), not hand-written spans.'); process.exitCode = 1; return; } // Payload mirrors the web editor's confirmPublish() exactly: // interactive components live inside the rich `content` HTML only. // Do NOT send contentItems here - a partial items list makes the frontend // switch to structured rendering and drop the post text. const postData = { type: 'text', content, ...(locInfo ? { lng: locInfo.lng ...[truncated 2578 chars]
- Remediation
- ## Remediation Suggestions - Require an executable confirmation gate before any image upload or publication. - Generate a short-lived approval artifact after displaying the final preview. - Bind the approval to: - The authenticated account identifier. - A cryptographic digest of the final HTML. - The complete manifest and local image list. - The location and exposure setting. - An expiration time and single-use nonce. - Validate and consume the approval artifact inside `publish.js`; reject execution if it is missing, expired, reused, or does not match the final draft. - Place the confirmation check before `uploadLocalImages()` so no local file leaves the machine before approval. - For direct interactive use, display the account, destination, location, and image list and require an explicit confirmation. - Provide a dry-run mode that performs validation and displays the proposed operation without uploading or publishing.
