Back to skill

Security audit

Legal/TOS Diff-er

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent legal-document tracking purpose, but its tool commands and network fetching expose users to command injection, unrestricted URL fetching, and unsafe deletion risks.

Review before installing. Only use this skill in a constrained environment with trusted inputs, because a crafted URL or label may execute unintended shell commands, arbitrary URLs may reach internal services, fetched pages can influence the generated analysis prompt, and removing a URL can permanently delete more snapshot data than intended.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
SKILL.md:16
Finding

Shell Command Injection Through Skill Arguments

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
handler.js:58
Finding

Server-Side Request Forgery Through Unrestricted URL Fetching

Content
View full analysis
import('node-fetch').then(m => m.default(...args)); let response; try { response = await fetch(url, { headers: { 'User-Agent': 'LegalTOSDiffBot/1.0 (Terms of Service Monitor)' }, follow: 10, timeout: 30000 }); } catch (err) { return `**Error fetching ${url}:** ${err.message}`; } if (!response.ok) { return `**Error:** HTTP ${response.status} ${response.statusText} for ${url}`; } const html = await response.text(); const { text, metadata } = await extractor.extractLegalText(html, url); ``` The same unrestricted request pattern appears in `cmdDiff`: ```js const registry = storage.loadRegistry(getDataDir()); const entry = storage.findEntry(registry, url); const label = entry ? entry.label : url; // Fetch current version const fetch = (...args) => import('node-fetch').then(m => m.default(...args)); let response; try { response = await fetch(url, { headers: { 'User-Agent': 'LegalTOSDiffBot/1.0 (Terms of Service Monitor)' }, follow: 10, timeout: 30000 }); } catch (err) { return `**Error fetching ${url}:** ${err.message}`; } ``` ### Technical Analysis Both `fetch` and `diff` accept a caller-controlled URL and issue a server-side request without validating the protocol, hostname, resolved address, port, or redirect destinations. Redirect following is enabled for up to ten hops. The documented requirement that the URL “must be a tracked URL” is not enforced. When `storage.findEntry()` returns no entry, execution continues and uses the submitted URL as the label. Consequently, an attacker does not need to add the target to the registry first. The implementation doe ...[truncated 1302 chars]
Remediation
View remediation

T01 · Skill Instruction Hijacking

Error
Location
prompts.js:22
Finding

Indirect Prompt Injection Through Remote Document Content

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
storage.js:127
Finding

Slug Prefix Collisions Cause Overbroad Snapshot Deletion

Content
View full analysis
f.startsWith(slug) && f.endsWith('.json') && f !== REGISTRY_FILE) .forEach(f => fs.unlinkSync(path.join(dir, f))); } ``` ### Technical Analysis Snapshot ownership is inferred from a lossy URL slug rather than from an exact, collision-resistant identifier. The transformation discards the URL scheme, query string, fragment, port, and many punctuation distinctions. Multiple different URLs can therefore produce the same slug. Deletion is even broader because it accepts every JSON filename beginning with the target slug. A short slug can be a prefix of another URL's slug, causing snapshots for unrelated tracked entries to be removed. ### Attack Path 1. An attacker adds or identifies two tracked URLs whose normalized slugs are identical or prefix-related. 2. Snapshots are created for both URLs in the shared `snapshots` directory. 3. The attacker invokes `remove` for the URL with the colliding or shorter slug. 4. `removeUrlData()` scans files using `startsWith(slug)`. 5. Snapshot files belonging to both the intended URL and the other tracked URL match. 6. The function deletes all matching files while removing only one registry entry. ### Impact Assessment An attacker able to manage tracked URLs can destroy historical snapshots for other documents and undermine the integrity and availability of legal-change records. The deletion i ...[truncated 152 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
handler.js:63
Finding

Unbounded Response Buffering and Ineffective Request Timeout

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code chunk only implements HTML content extraction for likely legal text. It does not retrieve documents from remote sources, store historical snapshots, compare versions, or classify legal changes into the declared categories. While extraction could be a supporting component of a larger ToS diffing system, this chunk’s actual behavior is materially narrower than the declared end-to-end functionality, so the description does not accurately represent what this code actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code chunk only contains prompt templates and prompt-building functions for legal document comparison. It supports the semantic diffing portion of the description and matches the stated categories closely. However, the declared description also claims the skill fetches Terms of Service documents and stores snapshots, while this code does not perform any network retrieval, persistence, or snapshot management. Because those are material capabilities in the declared purpose and are absent from the supplied code chunk, this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This code chunk supports part of the declared workflow—storing snapshots of document text and tracking URLs—but it does not implement the major advertised capabilities of fetching Terms of Service documents or analyzing differences semantically across legal-risk categories. The actual behavior is limited to local file storage, hashing, retrieval, listing, and deletion of snapshots. Because the declared description presents a broader end-to-end analysis skill while the code only provides storage utilities, the description does not accurately represent what this supplied chunk actually does.

Content

No source excerpt is available for this finding.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · analyzer.js (reported line 5)May include surrounding context.

js
function buildAnalysisOutput(oldSnapshot, newSnapshot, label) {
  if (!oldSnapshot) {
    return prompts.buildFirstSnapshotPrompt(newSnapshot.text, label);
  }

  if (oldSnapshot.source_hash === newSnapshot.source_hash) {

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · analyzer.js (reported line 12)May include surrounding context.

js
function buildAnalysisOutput(oldSnapshot, newSnapshot, label) {
  if (!oldSnapshot) {
    return prompts.buildFirstSnapshotPrompt(newSnapshot.text, label);
  }

  if (oldSnapshot.source_hash === newSnapshot.source_hash) {

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The remove command deletes a tracked URL and all associated snapshots immediately, with no confirmation prompt, dry-run mode, or explicit warning. In a skill whose primary value is historical legal document tracking, accidental or scripted invocation can irreversibly destroy audit history and comparison data, undermining integrity and availability of stored records.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: undici==7.24.7 — 12 advisory(ies): CVE-2026-6733 (undici vulnerable to HTTP response queue poisoning via keep-alive socket reuse); CVE-2026-13697 (undici vulnerable to cross-user information disclosure and parse-time crash via ); CVE-2026-16728 (undici vulnerable to downstream response desynchronization via retry interceptor) +9 more

High
Category
Supply Chain
Confidence
96% confidence
Finding

The lockfile pins transitive dependency undici to 7.24.7, and the supplied advisory data indicates this version is affected by multiple high-severity issues including HTTP response queue poisoning, information disclosure, and response desynchronization. Because this skill fetches remote Terms of Service documents over HTTP(S), a vulnerable HTTP client in the request path is directly security-relevant and could allow malformed or attacker-controlled responses to corrupt request/response handling or leak data across requests.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The removeUrlData function irreversibly deletes matching snapshot JSON files and removes the registry entry, but provides no confirmation prompt, user-facing log, or explanatory warning. Because this is a destructive operation on stored data, the absence of any disclosure is a clear missing-warning issue.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill exposes a removal operation that deletes tracked URLs and snapshots, but the manifest provides no warning, confirmation step, or safeguard against accidental destructive use. In a legal-document tracking workflow, loss of historical snapshots can erase auditability and prevent users from proving how terms changed over time.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The code reads the TOS_DATA_DIR environment variable to change where tracked data and snapshots are stored, but there is no user-facing output, comment, or docstring disclosing that storage location can be redirected via environment configuration. This affects where user data is written and could surprise users or operators troubleshooting data handling.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code creates directories and writes registry data to disk, but there is no confirmation prompt, log/print statement, or explanatory comment/docstring disclosing that persistent local files will be created and updated. Because file writes are a safety-relevant operation for code files, the lack of any visible user warning meets the missing-warning criterion.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The saveSnapshot function persists snapshot metadata and the full text content to local JSON files, which may include user or fetched data, yet the code contains no confirmation, visible disclosure, or explanatory comment/docstring. This is a file-write operation with potential data-retention implications, so it should be clearly disclosed to users.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
86% confidence
Finding

The dependency uses a caret range instead of an exact version, which allows newer package releases to be installed without explicit review. This creates supply-chain risk because a compromised or breaking upstream release could be pulled into the skill during future installs, especially since this skill fetches and processes untrusted remote web content.

Content

Scanner excerpt · package.json (reported line 7)May include surrounding context.

json
"description": "Fetches and semantically diffs Terms of Service documents",
  "main": "handler.js",
  "dependencies": {
    "cheerio": "^1.0.0",
    "node-fetch": "^3.3.0"
  },
  "engines": {

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
86% confidence
Finding

The dependency is not pinned to an exact version, so installation may resolve to different upstream releases over time. That increases supply-chain exposure if a malicious or vulnerable version of node-fetch is published, though the risk remains limited here because this file only declares the dependency and does not itself contain exploit logic.

Content

Scanner excerpt · package.json (reported line 8)May include surrounding context.

json
"main": "handler.js",
  "dependencies": {
    "cheerio": "^1.0.0",
    "node-fetch": "^3.3.0"
  },
  "engines": {
    "node": ">=18.0.0"

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
handler.js:21