Back to skill

Security audit

Auto Doc Index

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to generate documentation indexes as advertised, with practical cautions around the unpinned TypeScript runner and generated README diffs.

Before running the generator, pin `tsx` in the target project or use a lockfile-backed package script instead of ad hoc `npx`. Use it on trusted documentation inputs where possible, ensure each README has exactly one correctly ordered marker pair, and review generated README diffs before committing or publishing.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T08 · Insecure Dependencies

Warning
Location
SKILL.md:129
Finding
Unpinned Third-Party Package Execution Through npx<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:129`; also present in `README.md:35` and `template/generate-doc-index.ts:1,10-12` **Vulnerability Type**: Unpinned third-party dependency execution **Risk Level**: Medium ### Vulnerable Code ```bash npx tsx scripts/generate-doc-index.ts all ``` The template also uses `npx tsx` as its interpreter: ```typescript #!/usr/bin/env npx tsx /** * Usage: * npx tsx scripts/generate-doc-index.ts adr * npx tsx scripts/generate-doc-index.ts pitfall * npx tsx scripts/generate-doc-index.ts all */ ``` ### Technical Analysis The documented command invokes `tsx` through `npx` without specifying an exact version. The project does not include a package manifest, lockfile, or integrity information that would constrain which `tsx` package release is executed. If `tsx` is not already available locally, `npx` can retrieve it from the configured package registry and immediately execute its package code. This creates a supply-chain trust boundary that is not disclosed or controlled by the otherwise stated “zero external dependencies” design. The effective executable can change after the Skill has been reviewed. This is an insecure dependency-execution pattern rather than evidence that the current `tsx` package is malicious. ### Attack Path 1. A user copies the template into a repository and follows the documented `npx tsx` command. 2. No audited local version of `tsx` is installed. 3. `npx` resolves the package through the user's configured registry. 4. An attacker has compromised a resolved package release, the registry account, or the user's registry configuration. 5. `npx` downloads and executes the compromised package before or while running the generator. 6. The malicious package executes with the same operating-system permissions, environment variables, and repository access as the invoking user or CI worker. ### Impact Assessment A compromised dependency could execute arbitrary code with the in ...[truncated 465 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add `tsx` as an exact-version development dependency, for example: ```json { "devDependencies": { "tsx": "4.20.5" }, "scripts": { "generate-doc-index": "tsx scripts/generate-doc-index.ts all" } } ``` 2. Commit the generated lockfile and use a lockfile-enforcing installation command such as `npm ci`. 3. Invoke the audited local binary through a package script rather than permitting `npx` to download a missing package. 4. Replace the `#!/usr/bin/env npx tsx` shebang with a controlled execution method. 5. Where practical, compile the generator to JavaScript or rewrite it as standard Node.js JavaScript so no runtime TypeScript loader is required. 6. In CI, disable lifecycle scripts where compatible with the dependency model and restrict network access after dependency installation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
template/generate-doc-index.ts:52
Finding
Unescaped Document Metadata Injected Into Generated Markdown<![CDATA[ ## Vulnerability Details **File Location**: `template/generate-doc-index.ts:52-59,84-89,104-120` **Vulnerability Type**: Markdown content injection and generated-document integrity failure **Risk Level**: Medium ### Vulnerable Code Metadata is extracted without validation or output encoding: ```typescript const titleMatch = content.match(/^#\s+ADR-\d+:\s*(.+)$/m); const title = titleMatch?.[1]?.trim() ?? name; let status = 'unknown'; const statusLineMatch = content.match(/^Status:\s*(.+)$/im); if (statusLineMatch) { status = statusLineMatch[1].trim(); } ``` Pitfall fields are handled in the same way: ```typescript const titleMatch = content.match(/^#\s+PIT-\d+:\s*(.+)$/m); const title = titleMatch?.[1]?.trim() ?? name; const field = (key: string): string => { const m = content.match(new RegExp(`^\\*\\*${key}:\\*\\*\\s*(.+)$`, 'mi')); return m?.[1]?.trim() ?? '—'; }; ``` The values are then interpolated directly into Markdown: ```typescript function generateAdrTable(entries: AdrEntry[]): string { const sorted = entries.sort((a, b) => a.num.localeCompare(b.num)); const rows = sorted.map( (e) => `| ${e.num} | [${e.title}](${e.file}) | ${e.status} | ${e.date} |`, ); return [ '| ADR | Title | Status | Date |', '|-----|-------|--------|------|', ...rows, ].join('\n'); } function generatePitfallTable(entries: PitEntry[]): string { const sorted = entries.sort((a, b) => a.id.localeCompare(b.id)); const rows = sorted.map( (e) => `| [${e.id}](${e.file}) | ${e.title} | ${e.area} | ${e.severity} | ${e.status} |`, ); return [ '| ID | Title | Area | Severity | Status |', '|----|-------|------|----------|--------|', ...rows, ].join('\n'); } ``` ### Technical Analysis Titles, statuses, areas, severities, and filenames are treated as trusted Markdown fragments. Markdown table delimiters, link syntax, backslashes, brackets, and embedded HTML are not escaped or rejected. For example, a title cont ...[truncated 1749 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Introduce a dedicated Markdown table-cell escaping function: ```typescript function escapeTableCell(value: string): string { return value .replace(/[\r\n]+/g, ' ') .replace(/\\/g, '\\\\') .replace(/\|/g, '\\|') .trim(); } ``` 2. Escape link text separately from ordinary table cells, including brackets and backslashes: ```typescript function escapeLinkText(value: string): string { return escapeTableCell(value) .replace(/\[/g, '\\[') .replace(/\]/g, '\\]'); } ``` 3. Encode or validate generated link destinations. Prefer constructing links only from filenames that match a strict allowlist and URL-encode unsafe path characters. 4. Reject control characters and unexpected embedded HTML where HTML is not an intended metadata feature. 5. Apply reasonable maximum lengths to all parsed fields. 6. Add tests covering pipes, brackets, parentheses, backslashes, HTML tags, Unicode control characters, and malformed link syntax. 7. Run generation in verification mode during CI and review the resulting README diff before publication. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
template/generate-doc-index.ts:135
Finding
Unsafe README Replacement When Index Markers Are Reversed or Duplicated<![CDATA[ ## Vulnerability Details **File Location**: `template/generate-doc-index.ts:135-160` **Vulnerability Type**: Insufficient validation before destructive file write **Risk Level**: Low ### Vulnerable Code ```typescript const START_MARKER = '<!-- INDEX:START -->'; const END_MARKER = '<!-- INDEX:END -->'; function injectIndex(readmePath: string, table: string): void { if (!existsSync(readmePath)) { console.error(`README not found: ${readmePath}`); process.exit(1); } const content = readFileSync(readmePath, 'utf-8'); const startIdx = content.indexOf(START_MARKER); const endIdx = content.indexOf(END_MARKER); let updated: string; if (startIdx !== -1 && endIdx !== -1) { const before = content.slice(0, startIdx + START_MARKER.length); const after = content.slice(endIdx); updated = `${before}\n${table}\n${after}`; } else { console.error( `Markers not found in ${readmePath}. Add ${START_MARKER} and ${END_MARKER} around the index section.`, ); process.exit(1); } writeFileSync(readmePath, updated, 'utf-8'); console.log(`✅ Updated ${readmePath}`); } ``` ### Technical Analysis The function verifies only that each marker exists. It does not verify that: - The start marker occurs before the end marker. - Exactly one start marker exists. - Exactly one end marker exists. - The selected markers form the intended pair. If the end marker precedes the start marker, `before` contains content through the later start marker while `after` begins at the earlier end marker. Concatenating them can duplicate substantial portions of the README. With duplicated markers, `indexOf` silently chooses the first occurrence of each marker, potentially replacing a larger or different section than intended. The result is then written directly to the original file without an atomic temporary-file replacement or backup. ### Attack Path 1. A malformed merge, accidental edit, or malicious documentation contribution reverses o ...[truncated 978 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require exactly one occurrence of each marker. 2. Verify that the start marker appears before the end marker: ```typescript const starts = content.split(START_MARKER).length - 1; const ends = content.split(END_MARKER).length - 1; if (starts !== 1 || ends !== 1 || startIdx >= endIdx) { throw new Error( `Expected exactly one correctly ordered index marker pair in ${readmePath}`, ); } ``` 3. Validate the complete updated content before writing, including confirming that the non-index prefix and suffix remain unchanged. 4. Write to a temporary file in the same directory and atomically rename it over the target only after successful validation. 5. Optionally provide a `--check` or dry-run mode that reports the proposed diff without modifying files. 6. Add tests for missing, reversed, nested, and duplicated marker combinations. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The core intent broadly aligns with generating documentation index tables, but the description materially misrepresents how and what the code processes. It claims generation from file frontmatter and references ADR, RFC, Pitfall, etc., implying broader document-type support. In reality, the script supports only ADR and Pitfall directories, and it does not parse frontmatter at all. Instead, it uses regexes over filenames and markdown content/body fields to derive title, status, date, area, and severity. It also specifically edits README.md files between marker comments. These are material differences in capability and scope, so this should be flagged as a mismatch.

Hidden Instructions

High
Category
Prompt Injection
Content
## Boundaries

- This skill generates **index tables only** — it does not create or modify the content of individual documents.
- The generator script replaces content **only between `<!-- INDEX:START -->` and `<!-- INDEX:END -->` markers**. All other README.md content is preserved verbatim.
- Do NOT use this for indexes that require editorial curation (e.g., "recommended reading order"). Auto-generation is for factual, exhaustive catalogs.
- Do NOT introduce YAML frontmatter parsing libraries — the regex-based approach is intentional to keep the script zero-dependency.
- This skill targets file-system-based documentation. It does not apply to wiki-style or database-backed doc systems.
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
Each document is self-describing via frontmatter. A generator script scans
the directory, parses frontmatter, and injects the index table between
`<!-- INDEX:START -->` / `<!-- INDEX:END -->` markers in README.md.

**Write ops become N:N (each file independent). Index becomes a stateless pure function.**
Confidence
70% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Ae1

High
Category
analysis-evasion
Content
Copy `template/generate-doc-index.ts` from this skill's template directory,
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Copy `template/generate-doc-index.ts` from this skill's template directory,
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

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.

Rp1

Medium
Category
MCP Rug Pull
Confidence
84% confidence
Finding
The skill instructs users to execute `npx tsx`, which resolves and runs a package from the npm ecosystem unless already installed locally. Without pinning a version or constraining execution to a checked-in dependency, builds are exposed to supply-chain drift or a compromised upstream release, causing non-reproducible and potentially unsafe code execution.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
The shebang invokes `npx tsx` without pinning a specific version, so execution may fetch whatever `tsx` version is currently resolved from the registry or local environment. That creates a supply-chain risk where unexpected or compromised package versions could execute arbitrary code when the script is run.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding
The usage example tells operators to run `npx tsx scripts/generate-doc-index.ts adr`, which may download and execute an unpinned `tsx` package version. In environments that trust documentation verbatim, this can introduce avoidable supply-chain exposure and inconsistent behavior across runs.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding
This usage example repeats the same unpinned `npx tsx` pattern for the `pitfall` mode. Anyone following the instruction may execute an unverified package version, making the documentation itself a propagation point for supply-chain risk.

Rp1

Medium
Category
MCP Rug Pull
Confidence
90% confidence
Finding
The `all` mode example also relies on `npx tsx` without a version constraint, preserving the same risk of executing an unexpected package release. Because this is a utility likely used by maintainers in local environments or CI, the exposure is meaningful even though the script logic itself is simple.

Static analysis

No suspicious patterns detected.