Back to skill

Security audit

Rizz Coach

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent AI dating-text coach, but it needs Review because it sends private relationship text to Anthropic without a clear privacy notice and the simulator trusts caller-supplied conversation state.

Review before installing if users may paste real private chats, names, contact details, or sensitive relationship context. The skill should add clear third-party processing and privacy notices, validate simulator state, limit history size, check all generated text fields, and update or pin dependencies before broad use.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
src/skill.ts:133
Finding
Caller-Controlled Simulator State Enables Prompt Injection and Safety Bypass<![CDATA[ ## Vulnerability Details **File Location**: `src/skill.ts:133-138`, `src/modes/conversationSim.ts:103-125`, `src/core/prompts.ts:170-187` **Vulnerability Type**: Unvalidated client-controlled state and indirect prompt injection **Risk Level**: High ### Vulnerable Code ```ts // src/skill.ts:133-138 const existingState = payload.state as SimState | undefined; const personaName = optionalString(payload.personaName); const state: SimState = existingState ?? createSimSession(personaName); const simInput: ConversationSimInput = { userMessage, state }; const result = await sendSimMessage(simInput); ``` ```ts // src/modes/conversationSim.ts:103-125 const safety = checkInput(userMessage); if (!safety.safe) { throw new SafetyError(safety.message ?? "Safety check failed."); } const stateWithUserTurn: SimState = { ...state, turns: [ ...state.turns, { speaker: "user", message: userMessage }, ], }; const prompt = buildSimPersonaPrompt( state.persona, stateWithUserTurn.turns, userMessage ); ``` ```ts // src/core/prompts.ts:170-187 const history = turns .map((t) => `${t.speaker === "user" ? "User" : persona.name}: ${t.message}`) .join("\n"); return `You are playing a character in a flirting conversation simulator. The user is practicing their texting game. Your character: Name: ${persona.name} Personality: ${persona.archetype} Description: ${persona.description} Warmth level: ${persona.warmthLevel}/3 (1 = reserved/guarded, 2 = moderate, 3 = warm/open) Conversation so far: ${history || "(No messages yet — this is the opening)"} User just sent: "${userMessage}" ``` ### Technical Analysis The routing layer treats `payload.state` as a valid `SimState` through a TypeScript assertion. Type assertions provide no runtime validation, so a caller can supply arbitrary values for: - `state.persona.name` - `state.persona.archetype` - `state.persona.description` - Historical turn messages and speaker values - `state.currentScore` - `state.m ...[truncated 2194 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not accept authoritative simulator state directly from callers. Store state server-side and let clients submit only an opaque, unpredictable session identifier. 2. If client-provided state is unavoidable, validate it with a strict runtime schema such as Zod, TypeBox, or JSON Schema. 3. Resolve the persona from the built-in `PERSONAS` collection using a trusted identifier. Do not accept caller-defined persona descriptions or archetypes. 4. Validate all state properties: - Permit only `user` and `persona` speaker values. - Require finite numeric scores within documented ranges. - Require a recognized momentum value. - Require a Boolean `sessionOver`. - Reject unknown properties where practical. 5. Run safety checks over all text entering the prompt, including every historical turn and all persona-related fields. 6. Limit message length, history length, and total serialized state size. Retain only the most recent turns needed by the simulator. 7. Delimit untrusted content clearly, preferably using structured content such as JSON, and explicitly instruct the model that quoted state is data and must never be followed as instructions. 8. Consider signing serialized state with an HMAC if it must round-trip through an untrusted client. Verify the signature before use. 9. Add adversarial tests covering instructions embedded in persona descriptions, persona names, and historical messages. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/modes/conversationSim.ts:134
Finding
LLM-Generated Coaching Tips Bypass Output Safety Validation<![CDATA[ ## Vulnerability Details **File Location**: `src/modes/conversationSim.ts:134-151`, `src/modes/conversationSim.ts:176-180`, `src/core/formatter.ts:128-130` **Vulnerability Type**: Incomplete validation of model-generated output **Risk Level**: Medium ### Vulnerable Code ```ts // src/modes/conversationSim.ts:134-151 const raw = extractText(response.content); const parsed = parseSimResponse(raw); personaReply = parsed.reply; momentumDelta = parsed.momentumDelta ?? 0; coachingTip = parsed.coachingTip ?? undefined; } catch { console.warn("[conversationSim] LLM unavailable, using fallback reply."); personaReply = fallbackPersonaReply(state.persona, userMessage); } // ------------------------------------------------------------------ // 4. Safety check on persona reply // ------------------------------------------------------------------ const outputSafety = checkOutput(personaReply); if (!outputSafety.safe) { personaReply = "...I don't know what to say to that."; } ``` ```ts // src/modes/conversationSim.ts:176-180 return { personaReply, updatedState, feedback: coachingTip, }; ``` ```ts // src/core/formatter.ts:128-130 if (coachingTip) { lines.push("", `💡 Coach tip: ${coachingTip}`); } ``` ### Technical Analysis The Anthropic response contains multiple independently generated text fields. The application applies `checkOutput` only to `personaReply`; it does not apply the same validation to `coachingTip`. The unchecked value is returned through the public `feedback` property and rendered directly by `formatSimState`. Consequently, model output can reach the user without passing the documented output-safety control. This issue is particularly exploitable in combination with the caller-controlled simulator state vulnerability. An attacker can direct the model to keep `reply` benign while placing prohibited, manipulative, or otherwise attacker-selected content in `coachingTip`. Since only `reply` is inspected, the unsafe content reache ...[truncated 1369 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply `checkOutput` to every model-generated string, including both `personaReply` and `coachingTip`. 2. Prefer validating the complete parsed response before copying any field into application state or returned results. 3. If any generated field fails validation, discard the entire model response or replace the affected field with a trusted fallback. 4. Require `coachingTip` to satisfy strict schema constraints, including: - String or null type. - A conservative maximum character length. - No terminal control characters. - No unexpected line breaks if only one sentence is intended. 5. Apply the same output policy categories consistently across all user-visible fields rather than using field-specific omissions. 6. Add tests where unsafe language appears only in `coachingTip` while `reply` remains benign. 7. Treat model-generated text as untrusted at rendering boundaries and sanitize control characters before terminal display. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (18)

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
93% confidence
Finding
The lockfile includes form-data 4.0.5, which is flagged for CRLF injection in multipart field names or filenames. If this skill constructs multipart requests using attacker-controlled names, an attacker could tamper with HTTP multipart boundaries/headers and potentially smuggle or alter request content sent to downstream services.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The Conversation Simulator explicitly states that session state carries the full conversation history, but the skill description provides no user-facing privacy warning about retention, handling, or visibility of that content. Because users are encouraged to paste intimate texting and flirting exchanges, this omission can lead to oversharing of sensitive personal or relational data without informed consent.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The CLI solicits free-form user message content and optional context for analysis without warning users not to paste sensitive personal, intimate, or regulated data. In a dating-assistant context, users are especially likely to enter private conversations, increasing the chance of unintended exposure to logs, downstream model providers, or support/debug channels.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This flow asks users to paste 'their message,' which directly encourages submission of third-party communications without any consent or privacy warning. Because the feature is designed around analyzing someone else's message, it raises elevated privacy and confidentiality concerns compared with generic text input.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The simulator accepts an ongoing stream of conversational content with no disclosure about sensitive-data handling, even though users may role-play or paste real chats over multiple turns. Multi-turn interaction increases the volume of potentially sensitive data exposed and can reveal patterns, identities, and relationship details over time.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The function sends the user's conversation content and accumulated chat context to Anthropic via `client.messages.create(...)` without any evidence in this file of consent, minimization, or a declared need for third-party processing. Because this mode handles free-form interpersonal messages, users may disclose sensitive personal or intimate content, making undisclosed external transmission a real privacy and data-handling risk.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This file transmits `userMessage` and prior turns to an external LLM service but does not include any explicit warning or notice to the user that their messages leave the local skill boundary. In a conversation-simulator context, that omission is especially risky because users may treat the interaction as private practice and reveal personal, romantic, or otherwise sensitive text.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
This function sends the user's message and optional context to Anthropic's external API for rewriting. In a text-rewrite skill this transmission is functionally necessary, but without explicit consent, disclosure, or data-handling constraints it still creates a real privacy and data-exposure risk if users provide sensitive content.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code forwards user-provided message and context to an external LLM, but this file contains no explicit disclosure, consent flow, or indication that sensitive user text may leave the local system. That makes accidental exfiltration of personal, confidential, or regulated data more likely, especially because 'context' may include more sensitive information than the main message.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code sends user-provided message and optional context to Anthropic's external API for scoring, but nothing in this file indicates consent, disclosure, minimization, or redaction before transmission. Because flirting messages and relationship context can contain sensitive personal data, this creates a real privacy/security issue if users are unaware their content leaves the local system or if secrets/PII are included in context.

Known Vulnerable Dependency: esbuild==0.27.3 — 1 advisory(ies): GHSA-g7r4-m6w7-qqqr (esbuild allows arbitrary file read when running the development server on Window)

Low
Category
Supply Chain
Confidence
89% confidence
Finding
The lockfile includes esbuild 0.27.3, which is flagged for a documented advisory affecting the esbuild development server on Windows with arbitrary file read exposure. In this package context it appears only as a devDependency transitively used by tsx, so the issue is real but its exploitability is limited to development workflows that actually run the vulnerable dev server on affected platforms.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "",
  "license": "ISC",
  "dependencies": {
    "@anthropic-ai/sdk": "^0.39.0"
  },
  "devDependencies": {
    "@types/node": "^25.4.0",
Confidence
93% confidence
Finding
The runtime dependency uses a caret version range, which allows newer minor/patch releases to be installed over time. This can introduce supply-chain risk and reduce build reproducibility if an upstream release is compromised or introduces breaking behavior.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@anthropic-ai/sdk": "^0.39.0"
  },
  "devDependencies": {
    "@types/node": "^25.4.0",
    "tsx": "^4.21.0",
    "typescript": "^5.9.3"
  }
Confidence
89% confidence
Finding
The development dependency @types/node is not pinned to an exact version, so installs may resolve to different package contents over time. While this is primarily a dev-time package, it still expands the supply-chain attack surface and weakens reproducibility.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "@types/node": "^25.4.0",
    "tsx": "^4.21.0",
    "typescript": "^5.9.3"
  }
}
Confidence
89% confidence
Finding
The tsx development tool is specified with a caret range, permitting automatic upgrades within the major version. Because it is executed during development, a compromised upstream release could affect local environments or CI pipelines.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"devDependencies": {
    "@types/node": "^25.4.0",
    "tsx": "^4.21.0",
    "typescript": "^5.9.3"
  }
}
Confidence
88% confidence
Finding
The typescript compiler dependency is unpinned, which can lead to non-reproducible builds and exposure to malicious or faulty upstream releases. As a build tool, it may influence compilation behavior in developer or CI environments.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The prompt requires the model to act as "Rizz Coach" and "speak like a sharp, funny friend," imposing a fixed communication style on all interactions. Under the policy, forcing a specific language or locale style without user choice can be a natural-language policy concern when no opt-in or alternative is offered.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
This TypeScript file contains multiple hard-coded user-facing messages and titles in English, such as fallback feedback and grading titles. Because the skill does not indicate any user opt-in or locale selection, it implicitly forces a specific language for output, which matches the language/locale policy concern.

Static analysis

No suspicious patterns detected.