T09 · Insecure Skill Coding Practices
- Location
src/modules/replyGenerator.ts:94- Finding
Required Safety Disclaimer Is Missing from Reply Generation Outputs
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md:22,SKILL.md:46,SKILL.md:190-191,src/types.ts:85-89,src/modules/replyGenerator.ts:94-106, andcli/main.ts:55-67
Vulnerability Type: Missing enforcement of a declared output safety control
Risk Level: LowThe Skill metadata declares
alwaysDisclaimer: true, and its documentation requires every output to include a disclaimer framed as “Based on what you shared...”. However, the reply-generation result type has no disclaimer field,generateReplies()does not return a disclaimer, and the CLI reply renderer does not display one.Vulnerable Code
src/types.ts:85-89:ts export interface ReplyOptions { goal: UserGoal; replies: ReplyOption[]; tip: string; }src/modules/replyGenerator.ts:94-106:ts export function generateReplies(context: ReplyContext): ReplyOptions { const template = TEMPLATES[context.userGoal]; const tones: ReplyTone[] = ['bold', 'chill', 'safe']; const replies: ReplyOption[] = tones.map(tone => ({ tone, text: template[tone].text, rationale: template[tone].rationale, })); return { goal: context.userGoal, replies, tip: template.tip, }; }cli/main.ts:55-67:ts function printReplies(options: ReturnType<typeof generateReplies>) { section('✏️ Reply Options'); for (const reply of options.replies) { const label = reply.tone === 'bold' ? '🔥 Bold' : reply.tone === 'chill' ? '😎 Chill' : '🙂 Safe'; print(`\n${label}`); print(` "${reply.text}"`); print(` → ${reply.rationale}`); } print(`\n💡 Tip: ${options.tip}`); }Technical Analysis
The implementation does not enforce the safety contract declared in
SKILL.md. Other user-facing modules return an explicit disclaimer, butgenerateReplies()returns actionable i ...[truncated 1550 chars]- Remediation
View remediation
Remediation Suggestions
-
Add a mandatory
disclaimer: stringproperty toReplyOptions:ts export interface ReplyOptions { goal: UserGoal; replies: ReplyOption[]; tip: string; disclaimer: string; } -
Define and return the required disclaimer from
generateReplies():ts const DISCLAIMER = 'Based on what you shared — these are general reply ideas, not facts about what the other person feels. Choose only what feels authentic and respectful to you.'; return { goal: context.userGoal, replies, tip: template.tip, disclaimer: DISCLAIMER, }; -
Update
printReplies()and the test renderer to displayoptions.disclaimer. -
Add automated assertions confirming that every public user-facing module returns a non-empty disclaimer beginning with the required framing.
-
Add a policy consistency test that compares mandatory Skill metadata controls with the fields and behavior of every public output type.
-
