Back to skill

Security audit

web-capture

Security checks for vulnerabilities and agentic risk

Overview

The skill appears purpose-aligned, but it deploys a scheduled code-running agent with repository write authority that processes public web content and relies on unpinned CLI execution paths.

Review before installing. Pin the Cargo CLI version, add an explicit rule that page and news content is untrusted data whose instructions must never be followed, confirm the GitHub connector is limited to the intended repository, and verify DOMAIN, PAGES, and COMPETITORS are public URLs you intend to store in the repo. Keep the PR-only gate and do not grant extra connectors or direct context-write capability unless you intentionally expand the skill.

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

Warning
Location
infra/agents/web-scribe.prompt.ts:236
Finding

Untrusted Web Content Is Processed by a Shell-Capable Autonomous Agent Without Prompt-Injection Isolation

Content
View full analysis

Vulnerability Details

File Location: infra/agents/web-scribe.prompt.ts, lines 236–257 and 292–297
Vulnerability Type: Indirect prompt injection through externally controlled web content
Risk Level: Medium

Relevant code:

typescript
## 4. Seed what is empty

Only for domains step 3 found empty, from the page files and the news:

- global/: positioning, value proposition, and offerings (what is sold, to
  whom, at what pricing shape). One file each.
- icp/: who the pages say the product is for, as one file tagged inferred,
  with at least one disqualifier: a company that looks like a fit and is
  not. The CRM verifies it later; say so in the file's Source section.
- alternative/: one file per competitor the pages, the news or COMPETITORS
  name, plus one for the status quo (what buyers do without any product).
- client/: one file per customer named on the pages, with the industry, size
  and use case the page states, and reference_permission: unknown.
- proof/: one file per atomic proof point on those pages (a metric, a quote,
  a result), citing its client/ file and the URL. confidence: hypothesis.
- signal/: one candidate per event that would make a company likely to buy,
  as the pages describe their buyers, with its detection written
  operationally. confidence: hypothesis.

Do not write persona/ or jtbd/: job titles and jobs come from evidence this
cookbook does not read.
typescript
Otherwise write outputs/<today>-web-capture/README.md with the frontmatter
that layer requires (its outcome: line reads "web capture: <n> files
added"), run the repository's context lint (npm run lint:context) and fix
what it reports, then open one branch and one pull request titled
"[web-capture] <first run | week of <today>>", committing the page, competitor
and news files with the context files. Do not merge it, and do not push to
the default branch.

...[truncated 3333 chars]

Remediation
View remediation

Remediation Suggestions

  1. Add an explicit, high-priority rule stating that all page and news content is untrusted data and that instructions, commands, tool requests, or policy text contained within it must never be followed.
  2. Parse extraction results into a strict schema and provide the synthesis stage only the fields required for analysis rather than unrestricted full-page content.
  3. Separate retrieval and interpretation from command execution. A non-agent helper should validate and write raw content, while a restricted synthesis step should produce only structured candidate findings.
  4. Enforce filesystem boundaries outside the model so that generated changes can only target approved paths such as context/, cadence/log/raw/web/, and the designated output directory.
  5. Replace general shell access with an allowlisted command interface. Permit only the exact Cargo, Git, lint, and pull-request operations required by the workflow.
  6. Validate the resulting diff before any push and reject modifications outside the approved path set or changes to existing protected context files.
  7. Run external-content processing in a sandbox without unrelated credentials, network access, or host filesystem access.
  8. Preserve mandatory human review and supplement it with automated checks that block unexpected executable files, workflow changes, configuration changes, or command-bearing output.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description is for a full workflow that gathers company/competitor/news data and updates a context repository over time. The supplied code only declares an Anthropic connector used to access a language model through Cargo's proxy. While such a connector could support the broader skill, this code chunk itself does not carry out the described behavior and instead serves an infrastructure/configuration purpose. Therefore the code's actual behavior is materially different from the declared primary purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This code chunk does not match the declared operational behavior. The description claims a full monitoring and repository-updating workflow, but the supplied code is only a small infrastructure declaration that creates a folder for agent resources. While such a folder could support the larger skill, the actual code shown does not perform any of the declared core functions. Therefore this is a material description-to-behavior mismatch.

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 124)May include surrounding context.

md
5. **Plan, then stop.** `node --import tsx evals/contract.mjs` from this skill's folder, then

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · evals/contract.mjs (reported line 50)May include surrounding context.

js
assert.equal((agent.spec.connectorActions ?? []).length, 0, "it posts nothing: its only output is the pull request");
  assert.equal((agent.spec.tools ?? []).length, 0, "no tool");
  assert.equal((agent.spec.capabilities ?? []).length, 0, "a context capability would be a write path that skips the pull request");
  const env = agent.spec.repository?.env ?? [];
  assert.equal((Array.isArray(env) ? env : Object.keys(env)).length, 0, "no env: the domain is in the prompt, and there is no credential");
});

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

An overly broad trigger phrase can cause the wrong skill to activate on ordinary user requests, especially because this skill is designed to inspect websites, competitors, news, and prepare repository changes. Misrouting a request can lead to unintended collection, unnecessary external queries, or unwanted repo modifications under the authority of the automation.

Content

No source excerpt is available for this finding.

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

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

md
**Derive before you ask.** An input with a lookup is looked up, not asked. Only the rows marked
_asked_ genuinely live in the operator's head.

| Input                                                     | Kind  | How it is answered                                                                                                                                                                              | Why it matters                                                                                                                 |
| --------------------------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| domain (`DOMAIN` in `infra/agents/web-scribe.prompt.ts`)  | value | **derived** from the workspace name, `context/global/` or the repository README. **asked** only when none of them names one                                                                     | Every file the agent writes is about this company; a wrong domain is a wrong knowledge base. The agent refuses the placeholder |
| pages (`PAGES`, same file)                                | value | **derived** from the site's sitemap: the sections that say what is sold, to whom, at what price, for which customers, and what shipped                                                          | A page not listed is a change never seen                                                                                       |

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

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

md
**Derive before you ask.** An input with a lookup is looked up, not asked. Only the rows marked
_asked_ genuinely live in the operator's head.

| Input                                                     | Kind  | How it is answered                                                                                                                                                                              | Why it matters                                                                                                                 |
| --------------------------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| domain (`DOMAIN` in `infra/agents/web-scribe.prompt.ts`)  | value | **derived** from the workspace name, `context/global/` or the repository README. **asked** only when none of them names one                                                                     | Every file the agent writes is about this company; a wrong domain is a wrong knowledge base. The agent refuses the placeholder |
| pages (`PAGES`, same file)                                | value | **derived** from the site's sitemap: the sections that say what is sold, to whom, at what price, for which customers, and what shipped                                                          | A page not listed is a change never seen                                                                                       |

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

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

md
**Derive before you ask.** An input with a lookup is looked up, not asked. Only the rows marked
_asked_ genuinely live in the operator's head.

| Input                                                     | Kind  | How it is answered                                                                                                                                                                              | Why it matters                                                                                                                 |
| --------------------------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| domain (`DOMAIN` in `infra/agents/web-scribe.prompt.ts`)  | value | **derived** from the workspace name, `context/global/` or the repository README. **asked** only when none of them names one                                                                     | Every file the agent writes is about this company; a wrong domain is a wrong knowledge base. The agent refuses the placeholder |
| pages (`PAGES`, same file)                                | value | **derived** from the site's sitemap: the sections that say what is sold, to whom, at what price, for which customers, and what shipped                                                          | A page not listed is a change never seen                                                                                       |

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

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

md
| Input                                                     | Kind  | How it is answered                                                                                                                                                                              | Why it matters                                                                                                                 |
| --------------------------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| domain (`DOMAIN` in `infra/agents/web-scribe.prompt.ts`)  | value | **derived** from the workspace name, `context/global/` or the repository README. **asked** only when none of them names one                                                                     | Every file the agent writes is about this company; a wrong domain is a wrong knowledge base. The agent refuses the placeholder |
| pages (`PAGES`, same file)                                | value | **derived** from the site's sitemap: the sections that say what is sold, to whom, at what price, for which customers, and what shipped                                                          | A page not listed is a change never seen                                                                                       |
| competitors (`COMPETITORS`, same file)                    | value | **derived** from `context/alternative/` when it is seeded, with each competitor's pricing and changelog URLs from their sitemaps. **asked** only to confirm which are worth watching every week | Competitors' pricing and launches are what a weekly read catches that the team does not already know. At most 20 URLs in all   |
| LLM connector and model (`infra/c
...[truncated 25 chars]

Whitespace Padding

Medium
Category
Prompt Injection
Confidence
70% confidence
Finding

Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Content

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

md
| Input                                                     | Kind  | How it is answered                                                                                                                                                                              | Why it matters                                                                                                                 |
| --------------------------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| domain (`DOMAIN` in `infra/agents/web-scribe.prompt.ts`)  | value | **derived** from the workspace name, `context/global/` or the repository README. **asked** only when none of them names one                                                                     | Every file the agent writes is about this company; a wrong domain is a wrong knowledge base. The agent refuses the placeholder |
| pages (`PAGES`, same file)                                | value | **derived** from the site's sitemap: the sections that say what is sold, to whom, at what price, for which customers, and what shipped                                                          | A page not listed is a change never seen                                                                                       |
| competitors (`COMPETITORS`, same file)                    | value | **derived** from `context/alternative/` when it is seeded, with each competitor's pricing and changelog URLs from their sitemaps. **asked** only to confirm which are worth watching every week | Competitors' pricing and launches are what a weekly read catches that the team does not already know. At most 20 URLs in all   |
| LLM connector and model (`infra/c
...[truncated 25 chars]

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding

The prompt instructs the agent to fall back to npx --yes @cargo-ai/cli, which fetches and executes the latest package from the registry at runtime without a pinned version or integrity control. If the package is compromised, typosquatted, or a malicious version is published, the agent could execute attacker-controlled code in the repository environment with access to local files, git state, and any available credentials.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README explicitly describes automated network access to company and competitor websites and automatic writes of fetched content into repository paths, but it does not clearly warn operators that deployment will cause external web requests and persistent data writes. In a skill that runs weekly and opens pull requests, this can lead to unintended collection, storage, and review of third-party content or sensitive internal pages if the configured URLs are wrong or overly broad.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.