Back to skill

Security audit

agentic-engagement

Security checks for vulnerabilities and agentic risk

Overview

This skill is purpose-aligned, but needs Review because it deploys a persistent email agent that can automatically send replies from a workspace mailbox based on inbound lead emails.

Review before installing. Use this only if you want a persistent agent to send lead replies from a real workspace mailbox, and verify that Cargo or your deployment wraps sendEmail with hard recipient/thread limits, one-send-per-wake enforcement, and safe memory handling. Approve the recurring mailbox/domain costs explicitly and test adversarial replies before production 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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
infra/agents/engager.ts:43
Finding

Untrusted inbound email can influence an autonomous agent with email-sending authority

Content
View full analysis

Vulnerability Details

File Location: infra/agents/engager.ts:43-59; related policy instructions in infra/agents/engager.prompt.ts:32-38,46-51,75-80,107-113
Vulnerability Type: Prompt injection across an external-content trust boundary
Risk Level: High

Vulnerable Code

ts
systemPrompt: engagerPrompt,
capabilities: ["memory", { slug: "context", config: { isReadOnly: true } }],
uses: [
  { ref: sendEmail, config: { mailboxUuid: rep.uuid } },
  listEmailEvents,
],
triggers: [
  agentNativeTrigger({
    agentSlug: "email",
    name: "status",
    config: { kinds: ["replied", "unsubscribed"] },
  }),
],
heartbeat: {
  intervalMinutes: 1440,
  maxMessages: 16,
  prompt: engagerHeartbeatPrompt,
},

The corresponding prompt relies on natural-language restrictions:

ts
1. **Native email trigger** — inbound status. It fires when the thread's
   status becomes `replied` (they wrote back) or `unsubscribed` (they opted
   out). That is the event half of the loop.
2. **Heartbeat** — silence. A thread that stays at `sent` never fires the
   trigger. The heartbeat re-wakes this chat so you can call
   `listEmailEvents` and decide. It is a status check, not a reason to
   invent a first send or to run a sequence.
ts
Read the workspace context (ICP, product, objections, proof) before you claim
anything about what you sell. A fact that is not in context, not in this
thread, and not something the lead just told you is something you do not say.

On a heartbeat, or whenever this chat does not already carry a fresh event
payload, call `listEmailEvents` with this thread's uuid before you decide.
ts
Call `sendEmail` once. The mailbox is already bound; do not pass a different
one. For any message that continues a thread — a wake from the native email
trigger, or a heartbeat follow-up on a thread you already started — pass:

- `inReplyTo`: the Messag
...[truncated 3370 chars]
Remediation
View remediation

Remediation Suggestions

  1. Place a deterministic authorization wrapper in front of sendEmail rather than exposing the unrestricted native action directly to the model.
  2. Derive and lock the recipient, thread UUID, inReplyTo, and references values from the authenticated trigger payload. Do not allow the model or email body to supply or replace these fields.
  3. Reject attempts to add recipients, change recipients, start a new thread, or send outside the triggering conversation.
  4. Enforce the one-send-per-wake rule and follow-up limit in code or platform policy rather than only in the system prompt.
  5. Separate inbound email content from trusted instructions using structured fields, and clearly label the body as untrusted data that must never define tool parameters or authorization decisions.
  6. Expose only an allowlisted, minimal subset of workspace context to externally triggered runs. Keep secrets, credentials, internal instructions, and unrelated customer data unavailable.
  7. Disable persistent memory writes for reply-triggered and heartbeat-triggered executions unless they are strictly required. If memory writes are needed, validate them against a schema and reject instruction-like content derived from inbound email.
  8. Add policy tests using adversarial email replies that attempt recipient changes, context extraction, additional sends, suppression bypasses, and memory modification. Verify that the tool layer rejects these actions independently of model behavior.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description promises deployment of an end-to-end lead email conversation agent with mailbox, agent behavior, native triggers, and periodic checking. The supplied code chunk implements only one supporting infrastructure piece: adopting/defining a domain. While domain setup is consistent with part of the description, the actual code's primary behavior is much narrower and does not realize the main declared functionality. Therefore, this code chunk does not accurately represent the declared purpose.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 138)May include surrounding context.

md
- `node --import tsx evals/contract.mjs` passes against the adapted graph

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 170)May include surrounding context.

md
- `node --import tsx evals/contract.mjs` passes against the adapted graph

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
81% confidence
Finding

This code enables sendEmail, which can transmit user or system-derived data externally, but the file does not include a confirmation prompt, user-facing log/print, or explicit warning comment/docstring about that outbound action. The description says the agent holds email conversations with leads, but it does not clearly warn about the external transmission behavior or its implications.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

Lines L53-L58 state that warm-up is 'not in the project spec' while immediately providing concrete commands to perform mailbox warm-up. This does not change runtime code behavior directly, but it is an intent/documentation divergence because the text frames warm-up as out of scope while still prescribing it as part of operating the skill.

Content

No source excerpt is available for this finding.