Back to skill

Security audit

Grade-A Pipeline

Security checks across malware telemetry and agentic risk

Overview

The skill is a coherent multi-agent repository workflow, but it needs review because it grants broad local mutation authority and contains unsafe command/path handling and under-enforced guardrails.

Install only after reviewing the workflow adapter and run it only on a backed-up, trusted repository path in a host with filesystem, credential, network, time, and spending limits. Do not point it at arbitrary non-Git directories, keep remote push disabled unless explicitly intended, inspect generated branches/tags/MAP.md before merging, and treat cleanup as risky until path validation is added.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
examples/grade-a-pipeline.workflow.js:72
Finding
Shell Command Injection Through Unquoted Dynamic Prompt Values<![CDATA[ ## Vulnerability Details **File Location**: `examples/grade-a-pipeline.workflow.js:72-76, 321-343, 383, 416, 493` **Vulnerability Type**: Command injection through unsafe construction of shell instructions **Risk Level**: Critical ### Vulnerable Code ```javascript const REPO = args?.repoPath || '.' const REQUEST = args?.request || 'Harden the codebase: fix obvious bugs, add missing tests, tidy.' const MAX_WAVES = args?.maxWaves || 8 const GRADE_BAR = args?.gradeBar || 'A****' const MAX_REVIEW_ROUNDS = args?.maxReviewRounds || 3 ``` ```javascript const baselinePrompt = (testCmd) => 'You are the BASELINE tester. Establish the pre-change test result on a read-only worktree.\n' + READ_WT('baseline') + '\n' + 'Run the full test suite: ' + testCmd + '\nReturn SUITE (passed, total, failed, summary, tail). ' + 'If the suite is already red, report exactly which tests fail — that is the baseline the pipeline must not worsen.'; ``` ```javascript 'CHECKPOINT: git add -A && git commit -m "gap ' + task.id + ' (' + variant + '): ' + task.title + '". ' + ``` ```javascript const TEST_CMD = boot.test_command; ``` ### Technical Analysis The workflow concatenates several values into instructions that tell general-purpose agents to execute shell commands: - `args.repoPath` is inserted after `git -C` and into worktree paths without shell quoting or canonicalization. - `boot.test_command` is produced by an agent after inspecting repository-controlled configuration and is later inserted verbatim into executable instructions. - Planner-produced task IDs and titles are inserted into Git commit commands. - Similar unsafe interpolation is used for lint commands, branches, tags, and worktree operations. These values cross trust boundaries but are not passed through a structured process API, escaped for a shell, validated against an allowlist, or rejected when they contain shell metacharacters. Although the JavaScript adapter does not directly invoke a shell, it ...[truncated 1695 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace generated shell command strings with a trusted host API that accepts an executable and argument array separately. 2. Canonicalize `repoPath` and require it to be an absolute path under an explicitly approved root. 3. Reject paths containing control characters, NUL bytes, or values that fail canonical containment checks. 4. Do not accept an arbitrary test command from an agent. Detect supported package managers programmatically and map recognized project metadata to an allowlisted executable and argument list. 5. Require explicit user approval before running any repository-defined script. 6. Validate task IDs against a restrictive identifier pattern and pass commit messages as distinct process arguments. 7. Run build and test commands in a sandbox with restricted filesystem, credential, process, and network access. 8. Add adversarial tests covering spaces, quotes, command substitutions, separators, traversal sequences, and newline injection in every interpolated field. ]]>

T01 · Skill Instruction Hijacking

Error
Location
examples/grade-a-pipeline.workflow.js:219
Finding
Indirect Prompt Injection Through Repository-Derived Maps and Diffs<![CDATA[ ## Vulnerability Details **File Location**: `examples/grade-a-pipeline.workflow.js:219-230, 261-280, 285-310, 337, 401-425` **Vulnerability Type**: Indirect prompt injection from untrusted repository content **Risk Level**: High ### Vulnerable Code ```javascript const mapBlock = (MAP) => '\n\n===== CODEBASE MAP (authoritative; do not re-derive) =====\n' + MAP + '\n===== END MAP =====\n'; ``` ```javascript const cartographerPrompt = (shard, idx) => 'You are CARTOGRAPHER ' + idx + '. Read ONLY these files inside ' + INTEGRATION_WT + ' (a clean checkout of the repo). Do NOT modify anything.\n' + 'Files (relative to that worktree):\n' + shard.map(f => ' - ' + f).join('\n') + '\n\n' + 'For each file return: path, language, one-line role, and its top-level SYMBOLS (functions, classes, methods, ' + 'exported constants, routes/endpoints, CLI commands) with kind+name+signature+line, plus the modules it depends on. ' + 'Be precise and complete; this map is injected into every downstream worker. Return SHARDMAP.'; ``` ```javascript const reviewPrompt = (lens, MAP, diff) => 'You are a CODE REVIEWER with the ' + lens + ' lens. Review ONLY the cumulative diff below against the map. Find real, ' + 'specific defects in this lens; do not invent nits. ' + mapBlock(MAP) + '\nCUMULATIVE DIFF:\n' + diff.slice(0, 150000) + '\nReturn REVIEW (findings[] with file, issue, severity, fix).'; ``` ### Technical Analysis The workflow reads attacker-controllable repository files, converts their contents into a synthesized map, and inserts that map into numerous downstream prompts. It explicitly labels the map as “authoritative.” Repository-derived diffs are likewise inserted into reviewer and grading prompts. No robust trust-boundary instruction tells downstream agents that repository text is untrusted data and that embedded directives must never alter tool use, safety rules, output schemas, or task objectives. Delimiters identify where the map begins and e ...[truncated 1778 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all repository content, maps, diffs, test output, and planner output as untrusted data. 2. Add a higher-priority instruction stating that embedded repository instructions must never be followed and may only be analyzed as data. 3. Remove the “authoritative” designation from repository-derived free text. 4. Prefer structured representations containing normalized file paths, symbol names, and dependencies instead of copying free-form prose. 5. Strip or encode control-like text before inserting repository content into prompts. 6. Separate content-processing agents from mutation-capable agents and grant each the minimum required tools. 7. Disable network and out-of-scope filesystem access for cartography, planning, review, and grading agents. 8. Independently verify every proposed diff, changed path, command result, and Git operation outside the model. 9. Add prompt-injection fixtures to tests, including hostile source comments, Markdown instructions, filenames, test output, and diff content. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
examples/grade-a-pipeline.workflow.js:436
Finding
Destructive Cleanup Command Uses an Unvalidated Agent-Supplied Run Identifier<![CDATA[ ## Vulnerability Details **File Location**: `examples/grade-a-pipeline.workflow.js:436-442, 491-500, 627` **Vulnerability Type**: Unsafe path construction leading to command injection or arbitrary deletion **Risk Level**: Critical ### Vulnerable Code ```javascript const cleanupPrompt = () => 'Clean up the pipeline WORKTREES, KEEPING every branch and tag. Run:\n' + ' git -C ' + REPO + ' worktree remove --force ' + INTEGRATION_WT + ' 2>/dev/null || true\n' + ' git -C ' + REPO + ' worktree prune\n' + ' rm -rf ' + WT_ROOT + ' 2>/dev/null || true\n' + 'Do NOT delete any gap/ branch or tag. Then run git -C ' + REPO + ' worktree list and report it briefly as plain text.'; ``` ```javascript const RUN_ID = boot.run_id; BASE_BRANCH = boot.base_branch || 'main'; INTEGRATION_BRANCH = boot.integration_branch || ('gap/' + RUN_ID + '/integration'); BRANCH_PREFIX = 'gap/' + RUN_ID; WT_ROOT = REPO + '/.gap-worktrees/' + RUN_ID; INTEGRATION_WT = WT_ROOT + '/integration'; ``` ```javascript const cleanup = await agent(cleanupPrompt(), { label: 'cleanup', phase: 'Grade', agentType: 'general-purpose' }); ``` ### Technical Analysis `boot.run_id` is returned by a general-purpose bootstrap agent. Although the prompt asks for a timestamp, the workflow never validates that the returned value matches the expected format. The unvalidated value is concatenated with the also-unvalidated `REPO` to create `WT_ROOT`. Cleanup then embeds `WT_ROOT` directly after `rm -rf` without shell quoting, canonical path resolution, or a containment check. Consequently, traversal components, whitespace, newlines, shell operators, or command substitutions can change the deletion target or append another command. The cleanup agent is merely instructed to run the resulting string. There is no programmatic guarantee that deletion remains under `<repo>/.gap-worktrees`. ### Attack Path 1. A malicious repository prompt-injects the bootstrap agent, or the agent otherwise retur ...[truncated 1056 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce `run_id` with a strict pattern such as `^\d{8}-\d{6}$` before using it in any path or ref. 2. Generate the run identifier inside trusted workflow code rather than accepting it from an agent. 3. Resolve `REPO`, the approved worktree root, and the cleanup target to canonical absolute paths. 4. Verify that the cleanup target is a strict descendant of `<repo>/.gap-worktrees` and is not equal to the repository root, filesystem root, home directory, or worktree parent. 5. Refuse cleanup if symlinks, traversal components, or containment inconsistencies are detected. 6. Replace `rm -rf` shell text with a trusted filesystem API operating on a validated path. 7. Run cleanup with minimal filesystem permissions and without network access. 8. Require confirmation or retain failed worktrees when validation cannot prove safe containment. 9. Add tests for malicious run IDs, spaces, quotes, newlines, traversal sequences, symlinks, and shell operators. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:75
Finding
Unpinned Third-Party Installer and Mutable Skill Source<![CDATA[ ## Vulnerability Details **File Location**: `README.md:75-85` **Vulnerability Type**: Supply-chain exposure through unpinned installation sources **Risk Level**: Medium ### Vulnerable Code ```bash npx skills add AntreasAntoniou/grade-a-pipeline ``` The surrounding instructions state: ```text Read SKILL.md, then inspect the workflow adapter before granting it access to a repository. It needs Git, the project's build/test environment, and a compatible host exposing a Workflow launcher with agent, parallel, phase, log, and args. ``` ### Technical Analysis The documented installation command invokes `npx` without pinning the `skills` package to a specific immutable version. It also identifies the Skill by repository owner and name without an immutable commit, release digest, checksum, or signature. This creates two mutable supply-chain boundaries: 1. The npm-resolved installer can change independently of this audited project. 2. The repository content installed by that command can change after the audit. The finding does not demonstrate that either current source is malicious. The vulnerability is that the documented process does not ensure users receive the same code that was reviewed. ### Attack Path 1. The npm package, its maintainer account, the referenced repository, or a distribution dependency is compromised. 2. An attacker publishes or serves modified installer or Skill content. 3. A user runs the documented unpinned `npx` command. 4. The mutable installer retrieves or installs content different from the audited artifact. 5. The user grants the resulting Skill access to a source repository and an agent-enabled host. 6. Malicious instructions or code execute with the host’s available permissions. ### Impact Assessment A successful supply-chain compromise could obtain the same authority granted to the installed Skill, potentially including: - Reading and modifying source repositories. - Executing project build and test commands. - Acc ...[truncated 249 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the npm installer to a reviewed version, for example through an exact package version rather than a floating resolution. 2. Pin the Skill source to an immutable Git commit or signed release tag. 3. Publish cryptographic checksums for release artifacts and document verification before installation. 4. Sign releases and verify signatures in the installation workflow. 5. Recommend downloading and inspecting the exact pinned artifact before loading it into an agent host. 6. Use lockfiles or an equivalent integrity mechanism for installation tooling. 7. Document the trusted publisher identity and a procedure for responding to compromised releases. 8. Prefer a host sandbox with restricted network, credential, and filesystem access even after integrity verification. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
examples/grade-a-pipeline.workflow.js:543
Finding
Regression Gate and Maximum-Wave Limit Are Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `examples/grade-a-pipeline.workflow.js:74, 543-588` **Vulnerability Type**: Fail-open safety controls and ineffective execution limit **Risk Level**: Medium ### Vulnerable Code ```javascript const MAX_WAVES = args?.maxWaves || 8 ``` ```javascript const tasks = (finalPlan && finalPlan.tasks) || []; const waves = topoWaves(tasks); log('Plan: ' + tasks.length + ' tasks across ' + waves.length + ' dependency waves'); ``` ```javascript if (integ && integ.suite_passed === false) { escalations.push({ stage: 'wave' + waveNo, reason: 'regression', tail: integ.suite_tail }); log('REGRESSION GATE: wave ' + waveNo + ' reddened the suite — see escalations'); } ``` ### Technical Analysis `MAX_WAVES` is read from arguments but never applied to the computed `waves` array. A planner can therefore produce more execution waves than the configured limit. Likewise, a failed integration test is recorded as an escalation, but the loop is not stopped, paused, or rolled back. Subsequent batches and waves continue operating on the integration branch after a known regression. This conflicts with the stronger guardrail claim in `SKILL.md` that a wave which reddens the suite blocks the next wave. The README discloses these implementation limitations, but disclosure does not make the controls effective. Operators may still rely on the option name and guardrail language when granting consequential access. ### Attack Path 1. A planner, potentially influenced by repository prompt injection, creates an unexpectedly large dependency graph. 2. `topoWaves()` returns more waves than `MAX_WAVES`. 3. The workflow executes every returned wave because the configured limit is unused. 4. One wave introduces a regression and the integrator reports `suite_passed === false`. 5. The workflow only logs an escalation. 6. Later waves continue committing and testing against the already-broken integration branch. 7. Additional agent calls, mutat ...[truncated 687 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate `MAX_WAVES` as a finite positive integer within a conservative upper bound. 2. Reject a plan whose dependency graph exceeds the configured limit, or truncate only after obtaining explicit user approval. 3. Stop execution immediately when a regression gate fails. 4. Do not run later waves, hardening, grading, or checkpoint pushes until the failure is resolved or the user explicitly authorizes continuation. 5. Preserve the last known-good tag and reset or revert the failing integration changes before resuming. 6. Treat missing or malformed integration results as failures rather than success. 7. Add a host-level cap for total agent calls, elapsed time, token consumption, and mutation operations. 8. Update `SKILL.md` so its guardrail claims precisely match enforced behavior. 9. Add tests proving that oversized plans are rejected and that a failed suite prevents all subsequent wave execution. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (10)

Intent-Code Divergence

Medium
Confidence
87% confidence
Finding
The launch example says the live tree is 'read-only to agents; they clone it,' but the rest of the skill says agents work directly in in-repo git worktrees and persist branches/tags in the target repository. This inconsistency can mislead users about isolation boundaries and mutation scope, causing them to authorize the workflow under false assumptions about safety.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The bootstrap prompt explicitly tells an agent to run `git init`, `git add -A`, and `git commit` when the target path is not already a Git repository. That is a real integrity risk because a workflow presented as operating on an existing repo can silently convert any arbitrary directory into a repo and persist all current contents as a commit, changing user state beyond analysis or temporary execution.

Description-Behavior Mismatch

Low
Confidence
83% confidence
Finding
The cartography stage is not purely read-only: it writes `MAP.md` into both the integration worktree and the repository root, and commits it on the integration branch. In a skill that analyzes and modifies repos this is less severe than arbitrary code execution, but it is still an undisclosed repository mutation that can create noise, overwrite an existing file, or leak internal structure into version control.

Missing User Warnings

Low
Confidence
81% confidence
Finding
The skill states it may run 'git init' on a non-git target, which mutates the target directory and changes its state even before substantive work begins. Because the launch instructions do not foreground this behavior, a user could point the workflow at an arbitrary directory without realizing it will be converted into a git repository.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
At the operation site, the code instructs the agent to initialize and commit a non-Git directory without any explicit user confirmation gate. This creates a safety and consent problem because repository creation and committing all files are persistent mutations that may surprise the user or capture unintended material.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The bootstrap step modifies `.git/info/exclude` to hide `.gap-worktrees/` from status without a nearby confirmation or warning. While localized to Git metadata, it still changes repository behavior and can conceal generated paths from normal visibility, which reduces transparency for the user.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The workflow instructs writing `MAP.md` to the repository root with no explicit warning at the write site. Even if intended as a helpful artifact, this is a persistent workspace modification and may overwrite user files or introduce unreviewed documentation into the repo.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The cleanup prompt force-removes worktrees and then recursively deletes the computed worktree root with `rm -rf`, with no in-band guardrails at the point of deletion. Because the path is derived from user-controlled `repoPath`, mistakes, path confusion, or malformed values can cause destructive deletion of unintended directories.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
2. **Inspect the branch:** `git -C <repo> log --oneline <base_branch>..<integration_branch>` — one commit per task, one tag per wave. Diff any single agent's attempt via its `gap/<run>/task-*` branch.
3. **Run the suite yourself** on the branch — never trust a claimed-green you didn't see.
4. **Act on `unresolved_review`** blocking findings the harden loop didn't close.
5. **Merge when satisfied:** `git -C <repo> checkout <base_branch> && git -C <repo> merge --no-ff <integration_branch>`. The `checkout_instructions` field in the result gives the exact commands. (If a run crashed and left worktrees behind: `git -C <repo> worktree prune` and `rm -rf <repo>/.gap-worktrees`.)

`MAP.md` is committed on the integration branch and copied to the repo root during Cartography — keep it; it's the living map for the next run.
Confidence
93% confidence
Finding
The skill includes a cleanup command with `rm -rf <repo>/.gap-worktrees`, which is dangerous because `<repo>` is a parameterized path and destructive deletion is being suggested without safety checks. If `<repo>` is malformed, empty, unexpectedly broad, or user-substituted incorrectly, this can delete unintended data on the local filesystem.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
'Clean up the pipeline WORKTREES, KEEPING every branch and tag. Run:\n' +
  '  git -C ' + REPO + ' worktree remove --force ' + INTEGRATION_WT + ' 2>/dev/null || true\n' +
  '  git -C ' + REPO + ' worktree prune\n' +
  '  rm -rf ' + WT_ROOT + ' 2>/dev/null || true\n' +
  'Do NOT delete any gap/ branch or tag. Then run git -C ' + REPO + ' worktree list and report it briefly as plain text.';

// ---------------------------------------------------------------------------
Confidence
98% confidence
Finding
The command string concatenates `WT_ROOT` directly into `rm -rf `, creating a classic tool-parameter abuse sink. Since `WT_ROOT` is derived from `REPO`, which comes from `args.repoPath`, a crafted path containing shell metacharacters or whitespace can alter the command or broaden deletion scope, potentially leading to arbitrary filesystem deletion or command injection depending on the execution environment.

VirusTotal

VirusTotal findings are pending for this skill version.

View on VirusTotal

Static analysis

No suspicious patterns detected.