Back to skill

Security audit

axiv-html-cn-static

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent arXiv-to-local-HTML purpose, but its downloader and generated HTML handling expose users to avoidable network and active-content risks.

Review before installing. Run it only in an isolated virtual environment or container, avoid sensitive networks, and inspect generated HTML before opening or sharing it. Prefer a version that pins dependencies, restricts downloads to trusted arXiv HTTPS hosts with size limits, sanitizes embedded figure/table HTML, and uses local MathJax or an explicit CDN opt-in.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/arxiv_html_static_builder.py:128
Finding
Unrestricted Asset Retrieval Enables Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/arxiv_html_static_builder.py:128-147` **Vulnerability Type**: Server-Side Request Forgery and unrestricted network access **Risk Level**: High ### Vulnerable Code ```python def download_asset(url, assets_dir, manifest): if not url or url.startswith(("data:", "javascript:", "mailto:", "#")): return url if url in manifest: return manifest[url]["local"] try: resp = requests.get(url, headers={"User-Agent": USER_AGENT}, timeout=40) resp.raise_for_status() except Exception as exc: print(f"Asset fetch failed: {url}: {exc}", file=sys.stderr) return url filename = safe_asset_name(url, resp.headers.get("content-type", "")) local_path = assets_dir / filename local_path.write_bytes(resp.content) manifest[url] = { "url": url, "local": f"assets/{filename}", "bytes": len(resp.content), "content_type": resp.headers.get("content-type", ""), } return manifest[url]["local"] ``` Remote HTML and CSS supply URLs to this function through the following asset-rewriting operations: ```python for tag in soup.find_all(src=True): tag["src"] = download_asset(urljoin(base_url, tag["src"]), assets_dir, manifest) for tag in soup.find_all(srcset=True): tag["srcset"] = rewrite_srcset(tag["srcset"], base_url, assets_dir, manifest) for tag in soup.find_all(href=True): rel = {r.lower() for r in tag.get("rel", [])} if tag.get("rel") else set() should_fetch = tag.name == "link" and ("stylesheet" in rel or "icon" in rel or "preload" in rel) if tag.name in {"image", "use"}: should_fetch = True if should_fetch: abs_url = urljoin(base_url, tag["href"]) ``` ### Technical Analysis The Skill legitimately requires outbound network access to obtain an arXiv paper and its assets. However, the implementation does not restrict asset requests to arXiv-controlled hosts. Every supported ` ...[truncated 2655 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict downloadable assets to an explicit allowlist of trusted HTTPS hosts required for arXiv content. 2. Reject all schemes other than `https`. 3. Resolve each hostname before connecting and reject loopback, private, link-local, multicast, reserved, unspecified, and metadata-service addresses for both IPv4 and IPv6. 4. Disable automatic redirects or validate every redirect target using the same scheme, hostname, and resolved-address policy. 5. Stream responses instead of reading `resp.content` at once, and enforce strict per-file and aggregate download-size limits. 6. Limit the number of assets and CSS recursion depth processed for one paper. 7. Validate response MIME types against the expected resource category. 8. Apply equivalent validation to initial HTML, PDF, stylesheet, CSS asset, `srcset`, SVG, preload, and icon requests. 9. Fail closed rather than retaining a disallowed remote URL in generated HTML. 10. Where possible, use a network sandbox that permits access only to approved arXiv infrastructure. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/arxiv_html_static_builder.py:242
Finding
Unsanitized Remote Figure and Table Markup Is Embedded into Generated HTML<![CDATA[ ## Vulnerability Details **File Location**: `scripts/arxiv_html_static_builder.py:242-264, 389-407` **Vulnerability Type**: Stored HTML injection and active-content execution **Risk Level**: High ### Vulnerable Code ```python def clean_embedded_html(node, kind): frag = BeautifulSoup(str(node), "html.parser") for tag in frag.find_all(True): if tag.has_attr("style"): del tag["style"] if kind == "table": tag.attrs.pop("width", None) tag.attrs.pop("height", None) if kind == "table": unwrap_classes = { "ltx_transformed_outer", "ltx_transformed_inner", "ltx_inline-block", "ltx_flex_figure", "ltx_flex_cell", "ltx_figure_panel", "ltx_align_center", "ltx_centering", } for tag in list(frag.find_all(True)): classes = set(tag.get("class") or []) if classes & unwrap_classes and tag.name not in {"figure", "table", "thead", "tbody", "tr", "th", "td", "figcaption"}: tag.unwrap() for table in list(frag.find_all("table")): if table.find_parent(class_="table-scroll") is None: wrapper = frag.new_tag("div") wrapper["class"] = "table-scroll" table.wrap(wrapper) root = frag.find("figure") or frag.find("table") or frag return str(root) ``` The resulting remote markup is later inserted without output encoding or security sanitization: ```python def figure_html(figures): if not figures: return "" parts = ["<div class=\"paper-figures\">"] for fig in figures: label = fig.get("label") or fig.get("id", "figure") parts.append(f'<div class="paper-figure" data-label="{html.escape(label)}">') embedded = fig.get("html") if embedded: parts.append(embedded) else: parts.append("<figure>") for src in fig.get("images", []): parts.append(f' ...[truncated 2906 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the current layout-only cleaning logic with a strict HTML allowlist sanitizer. 2. Permit only tags required for static figures and tables, such as `figure`, `figcaption`, `div`, `span`, `table`, `thead`, `tbody`, `tr`, `th`, `td`, `img`, and a carefully reviewed subset of inert SVG tags. 3. Permit only necessary attributes such as safe classes, local `src` values, `alt`, `title`, `colspan`, and `rowspan`. 4. Remove all script-capable elements, embedded browsing contexts, plugin elements, forms, and metadata-refresh elements. 5. Remove every attribute beginning with `on`, regardless of capitalization. 6. Validate all URL-bearing attributes. Permit only expected local relative paths under `assets/`; reject `javascript:`, `data:` where unnecessary, remote HTTP URLs, protocol-relative URLs, and filesystem paths. 7. Treat SVG as active content. Either rasterize it, serve it only as an external image, or sanitize it with an SVG-specific policy. 8. Sanitize immediately before final HTML emission as well as during extraction, so modified figures JSON cannot bypass the policy. 9. Add regression tests containing script tags, event handlers, malicious SVG, iframes, forms, and dangerous URL schemes. 10. Consider applying a restrictive Content Security Policy to generated pages as defense in depth, while not relying on CSP instead of sanitization. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/requirements.txt:1
Finding
Open-Ended Dependency Versions Produce Non-Reproducible and Unreviewed Installations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/requirements.txt:1-2` **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```text requests>=2.28.0 beautifulsoup4>=4.11.0 ``` The documented workflow instructs users to install these requirements: ```bash python3 -m pip install -r scripts/requirements.txt ``` ### Technical Analysis Both dependencies use open-ended minimum-version constraints. An installation can therefore select any future release satisfying the lower bound rather than a version reviewed with this Skill. The package names are legitimate, and the audited files contain no evidence of dependency confusion, typosquatting, or an intentionally malicious package. Nevertheless, the configuration creates a supply-chain and reproducibility weakness: the effective code executed during installation and at runtime can change after the Skill itself has been audited. A compromised upstream release, compromised package-index account, or incompatible future release could consequently affect users without any change to this repository. ### Attack Path 1. A user follows the documented installation command. 2. The package resolver queries the configured Python package index. 3. It selects the latest releases satisfying the open-ended constraints. 4. If a selected release is compromised or introduces unsafe behavior, its installation or imported code executes in the user's environment. 5. The Skill then runs with the privileges of the invoking user. This is a conditional supply-chain path rather than evidence that the currently named dependencies are malicious. ### Impact Assessment A compromised dependency could obtain the same privileges as the Python process, potentially allowing it to: - Read or modify files accessible to the invoking user. - Access environment variables and process credentials. - Make arbitrary network requests. - Alter generated documents or assets. - Execute additiona ...[truncated 231 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin dependencies to explicitly reviewed versions rather than using open-ended lower bounds. 2. Generate and commit a lock file containing transitive dependency versions. 3. Record cryptographic hashes and install with `pip --require-hashes`. 4. Use a controlled package index or approved internal mirror where appropriate. 5. Scan locked versions for known vulnerabilities during continuous integration. 6. Review and deliberately update dependency pins on a scheduled basis. 7. Test dependency updates before publishing a new Skill release. 8. Avoid running package installation with administrative privileges. 9. Install and execute the Skill inside an isolated virtual environment or container with restricted filesystem and network access. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill clearly directs package installation, network fetching from arXiv, and creation of local output files, but it declares no explicit tool scope or permissions boundary. That increases the chance an agent will invoke network and filesystem actions without clear user awareness or policy gating, which is a real security and transparency problem even though it does not by itself prove malicious intent.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill description focuses on conversion functionality but does not prominently warn that it downloads remote content and writes a local directory tree containing HTML, assets, and metadata. In agent settings, missing disclosure of network and file-modifying behavior can lead to unexpected data transfer or filesystem changes without informed consent.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The documented commands instruct pip installation and execution of a downloader/builder script without an explicit warning that they will modify the local environment and access the network. Installing dependencies and running scripts are sensitive actions that can change the host state, introduce supply-chain risk, and surprise users if not clearly disclosed and consented to.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The skill promises a local/offline static HTML result but states that formula rendering uses the MathJax CDN by default. This creates a hidden remote dependency that can leak access metadata, break offline use, and undermine the trust model of a supposedly self-contained local artifact.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The document says external CDNs should not be a necessary dependency, yet earlier it makes a CDN the default for formula rendering. This inconsistency is dangerous because operators may believe output is offline-safe when it still depends on third-party infrastructure, creating avoidable privacy, integrity, and availability risks.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This markdown file repeatedly requires the generated artifact to be in Chinese, including the title and content requirements for a Chinese static HTML page and Chinese body text. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly justified.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The file description states the script builds a Chinese static HTML page, and the implementation later hard-codes Chinese language output and labels rather than offering a language choice. This is a natural-language locale policy concern because the skill forces a specific language experience without explicit user opt-in or configurable alternatives.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The asset downloader follows whatever URLs appear in source HTML/CSS and fetches them without restricting hostnames or schemes beyond a few trivial skips. A malicious or unexpected paper page could cause requests to arbitrary external servers, leaking the operator's IP/network metadata and importing untrusted content into the local build.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The generated document sets lang="zh-CN" and includes fixed Chinese title/meta text such as '中文静态版' and '本地中文静态 HTML'. Because the skill does not provide a user-selectable locale, it enforces a specific language setting in generated output.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The generated 'static' HTML loads MathJax from jsDelivr at runtime, which means opening the page causes a network request to a third-party CDN and executes remote JavaScript. That breaks the local/offline trust model and creates a supply-chain/privacy risk if the CDN is unavailable, compromised, or blocked.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.28.0
beautifulsoup4>=4.11.0
Confidence
96% confidence
Finding
The dependency specification uses a minimum version bound instead of pinning to an exact version, which makes builds non-reproducible and can result in different environments installing different releases. In a supply-chain context, this increases exposure to accidentally pulling a vulnerable or incompatible version of requests, especially given the package's history of advisories.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding
The manifest includes requests without pinning a specific version, and the package has multiple known advisories across its release history. Because the installed version is not fixed, there is no assurance that deployments will avoid affected releases, which creates a real supply-chain risk for a network-facing HTML retrieval/conversion skill that likely fetches remote arXiv content.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.28.0
beautifulsoup4>=4.11.0
Confidence
93% confidence
Finding
beautifulsoup4 is also unpinned, so installations may resolve to different versions over time. While this is usually a hygiene and supply-chain hardening issue rather than an immediate exploit, it weakens reproducibility and makes it harder to verify exactly what code is being deployed.

Static analysis

No suspicious patterns detected.