Back to skill

Security audit

Paper Cluster Survey 2.2

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent paper-review purpose, but its helper scripts can read arbitrary local text/HTML paths and fetch unrestricted URLs, which is broader than users may expect.

Install only if you trust the sources you will give it and can run it in a constrained environment. Avoid sensitive local paths or private PDFs unless you intend their contents to be processed, and prefer sandboxed filesystem and network egress controls because the scripts do not enforce those limits themselves.

Vulnerability Patterns
  • 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
  • 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 (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/extract-paper-records.mjs:278
Finding
Unrestricted Server-Side Request Forgery Through User-Controlled URLs and Redirects<![CDATA[ ## Vulnerability Details **File Location**: `scripts/extract-paper-records.mjs:278-291, 318-319` **Vulnerability Type**: Server-Side Request Forgery **Risk Level**: High ### Vulnerable Code ```js async function fetchSource(url) { const response = await fetch(url, { redirect: "follow", headers: { "user-agent": "paper-cluster-survey-v2-2/1.0", accept: "text/html,application/pdf;q=0.9,*/*;q=0.8", }, }); const contentType = response.headers.get("content-type") || ""; const buffer = Buffer.from(await response.arrayBuffer()); return { url: response.url, ok: response.ok, status: response.status, contentType, buffer, }; } ``` ```js async function extractFromUrl(record) { const notes = []; try { const fetched = await fetchSource(record.url); ``` ### Technical Analysis The extractor accepts any syntactically valid HTTP or HTTPS URL and passes it directly to `fetch()`. It does not restrict destination hosts, resolve and validate destination IP addresses, or reject loopback, private, link-local, multicast, and reserved address ranges. The `redirect: "follow"` option also causes redirects to be followed automatically without validating each redirect destination. Consequently, an initially public URL can redirect the request to an internal service even if validation is later added only to the original URL. Responses are captured in memory and subsequently processed as HTML, text, or PDF content. This makes the issue observable rather than blind SSRF: portions of a successfully retrieved internal response may appear in generated paper records. ### Attack Path 1. An attacker supplies a source such as a loopback, private-network, link-local, or public redirect URL. 2. The source is accepted because URL validation checks only for the `http:` or `https:` scheme. 3. `extractFromUrl()` passes the URL to `fetchSource()`. 4. `fetch()` connects using the network privileges of the Agent process and automa ...[truncated 918 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Introduce a centralized outbound URL validator that permits only required HTTP and HTTPS destinations. 2. Resolve hostnames before connecting and reject all addresses in loopback, private, link-local, multicast, unspecified, and reserved ranges for both IPv4 and IPv6. 3. Explicitly block common metadata destinations, including `169.254.169.254`, even when referenced through alternate numeric forms or DNS names. 4. Disable automatic redirects. Follow redirects manually and repeat scheme, hostname, port, and resolved-IP validation for every hop. 5. Consider an allowlist of recognized scholarly and publisher domains when the deployment model permits it. 6. Restrict destination ports to expected web ports. 7. Protect against DNS rebinding by ensuring the validated address is the address used for the connection. 8. Run extraction in a sandbox with restricted network egress and no access to internal management networks. 9. Avoid returning sensitive internal response bodies in extraction notes or generated records. 10. Add tests covering loopback addresses, RFC 1918 ranges, IPv6 local addresses, alternate IP encodings, redirects to private hosts, and DNS rebinding scenarios. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/extract-paper-records.mjs:404
Finding
Arbitrary Local-File Read Through Generic Path and Unvalidated Manifest Records<![CDATA[ ## Vulnerability Details **File Location**: `scripts/extract-paper-records.mjs:42-47, 71-74, 120-130, 404-420` **Vulnerability Type**: Arbitrary Local-File Read **Risk Level**: High ### Vulnerable Code ```js function inferKind(value) { if (isHttpUrl(value)) { const lower = value.toLowerCase(); if (lower.endsWith(".pdf") || lower.includes("arxiv.org")) { return "paper_url"; } return "url"; } if (value.toLowerCase().endsWith(".pdf")) { return "pdf"; } return "path"; } ``` ```js if (kind === "pdf" || kind === "path") { record.path = path.resolve(value); record.exists = fs.existsSync(record.path); record.title_hint = titleHintFromPath(value); return record; } ``` ```js function loadManifest(manifestFile) { const raw = fs.readFileSync(manifestFile, "utf8"); const parsed = JSON.parse(raw); if (Array.isArray(parsed)) { return parsed; } if (Array.isArray(parsed.sources)) { return parsed.sources; } throw new Error("Manifest JSON must be an array or an object with a sources array."); } ``` ```js if (record.kind === "path") { const lower = (record.path || "").toLowerCase(); if (lower.endsWith(".html") || lower.endsWith(".htm")) { const html = fs.readFileSync(record.path, "utf8"); return summarizeExtraction(record, { title: extractTitleFromHtml(html) || record.title_hint, authors: extractRepeatedMetaTag(html, "name", "citation_author"), year: extractYear(html), venue: extractMetaTag(html, "name", "citation_journal_title") || extractMetaTag(html, "name", "citation_conference_title"), abstract: extractAbstractFromHtml(html), text_excerpt: stripHtml(html).slice(0, 4000), extraction_method: "local-html", extraction_notes: [], pdf_url: extractMetaTag(html, "name", "citation_pdf_url"), }); } const text = fs.existsSync(record.path) ? fs.readFileSync(record.path, "utf8") : ""; ``` ### Technical Analysis Every non-URL input that ...[truncated 2110 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict local sources to explicitly supported paper formats rather than treating every non-URL input as a generic path. 2. Require local files to reside under one or more configured corpus directories. 3. Resolve each target with `fs.realpathSync()` and verify that the canonical path remains inside an authorized root. 4. Reject symbolic links or verify their canonical destinations before reading. 5. Use `fs.statSync()` to require a regular file and reject directories, devices, sockets, and other special files. 6. Apply an explicit extension and MIME-type allowlist, such as PDF and narrowly defined text or HTML formats. 7. Normalize and validate every manifest entry with the same validation logic used for direct command-line sources. 8. Define a strict manifest schema and reject unknown, missing, or inconsistent fields. 9. Do not trust caller-supplied `kind`, `exists`, or normalized `path` fields. 10. Run the extractor under a dedicated low-privilege account or filesystem sandbox with access only to the intended corpus and temporary directory. 11. Require explicit user confirmation before reading non-PDF local files. 12. Add tests for absolute paths, parent-directory traversal, symbolic-link escapes, special files, and crafted manifest objects. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/extract-paper-records.mjs:278
Finding
Unbounded Remote Response Buffering Allows Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/extract-paper-records.mjs:278-291` **Vulnerability Type**: Uncontrolled Resource Consumption **Risk Level**: Medium ### Vulnerable Code ```js async function fetchSource(url) { const response = await fetch(url, { redirect: "follow", headers: { "user-agent": "paper-cluster-survey-v2-2/1.0", accept: "text/html,application/pdf;q=0.9,*/*;q=0.8", }, }); const contentType = response.headers.get("content-type") || ""; const buffer = Buffer.from(await response.arrayBuffer()); return { url: response.url, ok: response.ok, status: response.status, contentType, buffer, }; } ``` ### Technical Analysis The extractor calls `response.arrayBuffer()`, which buffers the complete remote response before processing it. There is no: - Maximum response-size limit. - Validation of the `Content-Length` header. - Streaming byte counter. - Request or body-read timeout. - Abort mechanism. - Content-type allowlist enforced before buffering. Although subprocess output is separately constrained with a 10 MiB `maxBuffer`, the network response itself has no equivalent limit. An attacker-controlled server can return an extremely large body or transmit data indefinitely and slowly. ### Attack Path 1. An attacker supplies a URL under their control. 2. The extractor initiates a request without a deadline or body-size policy. 3. The server returns a very large response or continuously streams data. 4. `response.arrayBuffer()` attempts to retain the entire response in memory. 5. Memory consumption grows until the request fails, the Node.js process becomes unresponsive, or the process is terminated. 6. Because sources are processed sequentially, one malicious source can prevent the remaining corpus from being processed. ### Impact Assessment Successful exploitation can cause: - Excessive memory consumption. - Node.js process termination due to out-of-memory conditions. - Prol ...[truncated 296 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use `AbortController` to enforce connection, response-header, and body-read deadlines. 2. Inspect `Content-Length` when present and reject responses exceeding a configured maximum. 3. Stream the response body while counting bytes, aborting immediately when the maximum is reached. 4. Set separate, conservative size limits for HTML and PDF content. 5. Validate the response content type before reading the complete body. 6. Reject unsupported and ambiguous content types unless explicitly approved. 7. Apply limits to PDF page count, decompressed object size, processing time, and subprocess resource use. 8. Consider worker isolation with operating-system memory and CPU limits. 9. Handle oversized or timed-out sources as individual extraction failures so the remaining corpus can continue. 10. Add tests using oversized bodies, chunked responses without `Content-Length`, slow streams, compressed payloads, and redirect chains. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/extract-paper-records.mjs:335
Finding
Untrusted Remote Paper Content Is Promoted to Primary Agent Context<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:22-38, 137-139`; `scripts/extract-paper-records.mjs:335-376` **Vulnerability Type**: Indirect Prompt Injection **Risk Level**: Medium ### Vulnerable Code and Instructions ```md ### 2. Extract paper records before reasoning - Use `scripts/extract-paper-records.mjs` to turn PDFs and URLs into structured records before classification. - The extraction pass should gather as much of the following as possible: - `title` - `authors` - `year` - `venue` - `abstract` - `task` - `method` - `datasets` - `metrics` - `main_contribution` - `limitations` - `source` - `extraction_notes` - Treat extracted records as the primary context for classification and survey drafting. - If important fields are missing, only fall back to direct source reading for the specific missing details. ``` ```md ### `scripts/extract-paper-records.mjs` - Fetch URLs, resolve likely paper metadata, and extract paper text evidence from URLs or PDFs. - Prefer this script before asking the model to reason over a large source set. - Use its output as the main context object for classification and review drafting. ``` ```js const html = fetched.buffer.toString("utf8"); const title = extractTitleFromHtml(html); const abstract = extractAbstractFromHtml(html); const authors = extractRepeatedMetaTag(html, "name", "citation_author"); const venue = extractMetaTag(html, "name", "citation_journal_title") || extractMetaTag(html, "name", "citation_conference_title"); const year = extractYear( extractMetaTag(html, "name", "citation_publication_date") || extractMetaTag(html, "name", "citation_date") || html, ); const pdfUrl = extractMetaTag(html, "name", "citation_pdf_url"); const textExcerpt = stripHtml(html).slice(0, 4000); if (!title) { notes.push("HTML title metadata was missing or weak."); } if (!abstract) { notes.push("Abstract metadata was missing; only general page text was available."); } return summarize ...[truncated 2424 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add an explicit instruction to `SKILL.md` that all paper, PDF, metadata, and web-page content is untrusted data. 2. Direct the Agent never to follow commands, policies, tool instructions, or role changes found inside source material. 3. Clearly delimit extracted evidence using structured fields and labels such as `UNTRUSTED_SOURCE_CONTENT`. 4. Keep operational instructions separate from extracted source text at every stage of prompt construction. 5. Avoid placing raw page excerpts directly into the primary instruction context when structured metadata is sufficient. 6. Detect and flag instruction-like phrases in titles, abstracts, metadata, and excerpts for additional review. 7. Preserve provenance for every field so the Agent can distinguish source statements from trusted workflow rules. 8. Require human confirmation before acting on URLs, commands, credential requests, or tool-use directions originating in paper content. 9. Configure downstream prompts to summarize or quote the evidence without executing or obeying it. 10. Add adversarial tests containing prompt-injection text in HTML metadata, PDF text, abstracts, titles, and citation fields. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Ae1

High
Category
analysis-evasion
Content
- Use `scripts/normalize-sources.mjs` when the source set is mixed or should be stored as a reusable manifest.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- Use `scripts/normalize-sources.mjs` when the source set is mixed or should be stored as a reusable manifest.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- Use `scripts/extract-paper-records.mjs` to turn PDFs and URLs into structured records before classification.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- Use `scripts/extract-paper-records.mjs` to turn PDFs and URLs into structured records before classification.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
### `scripts/render-formal-review-template.mjs`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill explicitly instructs use of scripts that fetch external URLs and process paper sources, but it does not declare any tool permissions or allowed-tools scope. This creates a capability/expectation mismatch that can lead to unauthorized network access or execution in environments where users and reviewers cannot clearly see what external actions the skill may perform.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill tells the agent to fetch remote URLs and extract content from local PDFs without warning the user that document contents or derived metadata may be transmitted to external services. In a literature-review workflow, local PDFs may contain unpublished, licensed, or sensitive material, so silent network use increases confidentiality and compliance risk.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The skill allows implicit invocation without any visible activation scope, caller constraints, or narrowing conditions. That increases the chance the agent will auto-select this skill in contexts involving URLs or documents, causing unreviewed access to local PDFs or remote paper links and expanding the attack surface for prompt injection, unintended data processing, or exfiltration through fetched content.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The template includes fixed Chinese section headings in the "Typical review shape," which can steer the agent to produce output in Chinese even when the user did not request that language. This is a prompt-quality and instruction-alignment issue: it can cause unexpected language switching, reduce usability, and break downstream consumers that expect English or user-selected output, but it is not a direct code-execution or data-exfiltration risk.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
This skill's stated purpose is to extract structured paper records from files and URLs, which justifies file and network access, but not invoking arbitrary local executables via the shell. The code probes for and runs external tools (`pdftotext`, `mutool`, `python3`, `strings`) and executes inline Python, introducing a broader execution capability than is necessary from the manifest description alone.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script fetches arbitrary HTTP/HTTPS sources provided by the user, which transmits network requests and identifying request metadata such as the configured User-Agent. While this behavior is part of extraction, the code provides no confirmation prompt, log message, comment, or docstring warning that remote URLs will be contacted.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script hardcodes Chinese headings and template text such as "分类表", "摘要", and "基于给定文献语料的综述" in its generated output while the CLI help and surrounding code are otherwise in English. This creates a natural-language locale policy issue because users are not given an opt-in or alternative language selection.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The markdown file states that the skill extracts and reviews papers from local PDFs or URLs, which implies reading local files and fetching remote content. However, it does not warn users that local document contents may be processed or that URLs may trigger network access, which are behaviors that can affect privacy and system/network activity.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The documentation states that references are formatted in GB/T 7714, which imposes a specific locale/style convention. Because no opt-in, alternative style choice, or justification is provided, this may violate the language/locale policy for skills that should not force a specific locale-specific output format by default.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
This is a natural-language locale/style constraint that forces a specific regional bibliographic format. The file does not indicate that the style is optional, user-selected, or required for a particular compliance context, so it can violate language/locale choice expectations.

Missing User Warnings

Low
Confidence
82% confidence
Finding
When a remote source is a PDF, the script writes the downloaded content to a temporary file under the system temp directory before processing it. Although the file is later removed, the script does not disclose this filesystem write anywhere in help text, comments, or user-visible logging.

Missing User Warnings

Low
Confidence
83% confidence
Finding
This code performs a filesystem write when `--out` is provided, but there is no confirmation prompt, warning message, or inline comment/docstring disclosing that it will create or overwrite the target file. The help text describes normalization behavior but does not explicitly warn that a file path supplied via `--out` will be written.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/extract-paper-records.mjs:162