T09 · Insecure Skill Coding Practices
Note
- Location
- src/modules/replyGenerator.ts:94
- Finding
- Required Safety Disclaimer Is Missing from Reply Generation Outputs## 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`, and `cli/main.ts:55-67` **Vulnerability Type**: Missing enforcement of a declared output safety control **Risk Level**: Low The 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, but `generateReplies()` returns actionable i ...[truncated 1550 chars]
- Remediation
- ## Remediation Suggestions 1. Add a mandatory `disclaimer: string` property to `ReplyOptions`: ```ts export interface ReplyOptions { goal: UserGoal; replies: ReplyOption[]; tip: string; disclaimer: string; } ``` 2. 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, }; ``` 3. Update `printReplies()` and the test renderer to display `options.disclaimer`. 4. Add automated assertions confirming that every public user-facing module returns a non-empty disclaimer beginning with the required framing. 5. Add a policy consistency test that compares mandatory Skill metadata controls with the fields and behavior of every public output type.
