T09 · Insecure Skill Coding Practices
- Location
scripts/video_gen.js:161- Finding
Executable Does Not Enforce the Skill's Declared Model and Input Restrictions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/video_gen.js:161-164, 173-191, 195-212
Vulnerability Type: Missing server-request scope validation
Risk Level: MediumComplete Code Snippet
js function detectMode(params) { if (Array.isArray(params.images) && params.images.length > 0) return 'multi_image'; if (typeof params.image === 'string' && params.image) return 'image'; return 'text'; } function coerceBool(v, defaultVal = false) { if (v == null) return defaultVal; if (typeof v === 'boolean') return v; return String(v).toLowerCase() === 'true'; } function hasNonEmptyModel(params) { const m = params.model; return m != null && String(m).trim() !== ''; } function buildRequestBody(params) { const model = String(params.model).trim(); const body = { prompt: params.prompt, model, duration: Number(params.duration) || 5, }; if (params.aspect_ratio) body.aspect_ratio = params.aspect_ratio; if (params.resolution) body.resolution = params.resolution; if (params.generate_audio != null) body.generate_audio = coerceBool(params.generate_audio, false); if (params.negative_prompt) body.negative_prompt = params.negative_prompt; if (params.image) body.image = params.image; if (params.images) body.images = params.images.slice(0, 3); return body; } async function submitTask(params, apiKey) { if (!hasNonEmptyModel(params)) { return { ok: false, phase: 'failed', errorCode: 'MISSING_PARAM', errorMessage: "'model' is required in JSON parameters (no default model).", }; } const mode = detectMode(params); const pathMap = { text: '/v1/generation/text-to-video', image: '/v1/generation/image-to-video', multi_image: '/v1/generation/multi-image-to-video', }; const body = buildRequestBody(params); let res; try { res = await apiRequest('POST', pathMap[mode], body, apiKey);Technical Analysis
The Skill documentation limits execution to the `SEEDANCE ...[truncated 2246 chars]
- Remediation
View remediation
Remediation Suggestions
- Enforce the model in executable code:
js if (String(params.model).trim() !== 'SEEDANCE_2_0') { return validationError("Only SEEDANCE_2_0 is permitted by this Skill."); }- Reject
negative_promptandimagesrather than silently forwarding them. - Permit only text-to-video and single-image-to-video requests.
- Bind each command to its declared endpoint. For example,
submit-textmust rejectimageandimages, whilesubmit-imagemust require exactly oneimage. - Validate
imagewithnew URL()and require thehttps:protocol. If the threat model requires it, resolve and reject loopback, link-local, private-network, and otherwise non-public destinations. - Enforce the documented allowlists:
- Duration:
5,10, or15 - Aspect ratio:
9:16,1:1, or16:9 - Resolution:
480por720p - Prompt length: no more than 2,000 characters
generate_audio: strict Boolean input
- Duration:
- Reject unknown JSON fields so future or undocumented API capabilities cannot be reached accidentally.
- Perform validation before any paid API request and return a structured validation error.
- Add automated negative tests covering arbitrary models, multi-image payloads, forbidden fields, malformed URLs, local URLs, invalid durations, and command/mode mismatches.
