Back to skill

Security audit

Anne Library Downloader

Security checks for vulnerabilities and agentic risk

Overview

This skill is framed as an academic library downloader, but it asks users to handle institutional credentials while shipping incomplete download/authentication behavior and an exploitable helper that can execute injected JavaScript if used.

Review carefully before installing. Do not provide institutional credentials unless you are comfortable with the skill's incomplete authentication design and your institution permits this use. Avoid invoking or integrating the Playwright helper until the generated-script injection issue is fixed, and prefer installing dependencies in an isolated environment with pinned versions.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/download.py:83
Finding
JavaScript Injection in Generated Playwright Script Enables Arbitrary Command Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/download.py`, lines 83–114 **Vulnerability Type**: JavaScript injection resulting in arbitrary local command execution **Risk Level**: High ### Vulnerable Code ```python script = f''' const {{ chromium }} = require('playwright'); (async () => {{ const browser = await chromium.launch({{ headless: true }}); const context = await browser.newContext(); const page = await context.newPage(); // Navigate to URL await page.goto('{url}', {{ waitUntil: 'networkidle' }}); // Wait for content await page.waitForTimeout(2000); // Handle authentication if needed // (Would need credentials for institutional access) // Get PDF link or download directly const pdfLink = await page.$('a[href$=".pdf"]'); if (pdfLink) {{ const href = await pdfLink.getAttribute('href'); console.log(href); }} await browser.close(); }})(); ''' # Save and run script with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False) as f: f.write(script) temp_path = f.name try: result = subprocess.run(['node', temp_path], capture_output=True, text=True) return result.stdout.strip() finally: os.unlink(temp_path) ``` ### Technical Analysis The `download_with_playwright` function inserts the caller-provided `url` directly into executable JavaScript source using a Python formatted string. The URL is placed between JavaScript single quotes without escaping or safe serialization: ```python await page.goto('{url}', ...) ``` An attacker can supply a value containing a single quote and additional JavaScript statements. This can terminate the intended string, alter the generated program, and invoke Node.js APIs such as `require('child_process')`. The generated source is written to a temporary `.js` file and executed with Node.js. Although `subprocess.run` uses an argument array and therefore does not itself invoke a she ...[truncated 1911 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not construct executable JavaScript by interpolating untrusted values into source code. 2. Prefer the Python Playwright API so the URL remains data rather than generated program text. 3. If a separate Node.js process is required, place the fixed JavaScript in a reviewed static file and pass the URL as a distinct command-line argument, standard-input value, or environment variable. 4. If serialization into JavaScript cannot be avoided, use a proper serializer such as `json.dumps(url)` rather than manually adding quotes. 5. Validate the parsed URL before navigation: - Permit only required schemes such as `https`. - Reject embedded credentials and malformed URLs. - Apply an explicit hostname allowlist if only supported academic platforms are intended. 6. Add a subprocess timeout and check the exit status: ```python subprocess.run( ["node", static_script_path, validated_url], capture_output=True, text=True, check=True, timeout=30, ) ``` 7. Run browser automation with the minimum filesystem, network, and process permissions necessary. 8. Add regression tests containing quotes, backslashes, line breaks, and JavaScript-like URL input to ensure values cannot change program syntax. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:17
Finding
Unpinned Third-Party Packages and Browser Artifacts Create Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 17–22; related dependency declarations in `claw.json`, lines 9–13 **Vulnerability Type**: Unpinned and mutable third-party dependencies **Risk Level**: Medium ### Vulnerable Code Installation instructions in `SKILL.md`: ```bash # Install dependencies pip install playwright requests beautifulsoup4 # Install browser for Playwright playwright install chromium ``` Related dependency declarations in `claw.json`: ```json "dependencies": { "playwright": ">=1.40.0", "requests": ">=2.28.0", "beautifulsoup4": ">=4.12.0" } ``` ### Technical Analysis The documented installation command installs the latest versions available from the configured Python package index. The project does not provide an exact-version lock file or cryptographic hashes. The `claw.json` declarations also use open-ended minimum constraints, permitting any future release above the specified version. In addition, `playwright install chromium` downloads a browser artifact externally without documenting a fixed browser revision or an independent integrity-verification procedure. Consequently, the artifacts executed by users may differ from those reviewed during this audit. This is a supply-chain hardening issue rather than evidence that the named packages are currently malicious. The risk arises because mutable package and browser releases can introduce compromised code, unexpected installation behavior, or incompatible changes after the skill itself has been reviewed. ### Attack Path 1. A user follows the installation instructions or a deployment system resolves the declared dependencies. 2. The package manager selects versions available at installation time rather than an audited, immutable dependency set. 3. A compromised package-index account, malicious future release, unsafe alternate package source, or compromised artifact distribution channel supplies altered code. 4. The package or browser artifact is instal ...[truncated 961 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to an exact reviewed version rather than using unconstrained installation commands or open-ended minimum versions. 2. Generate and commit a dependency lock file that also resolves transitive dependencies. 3. Use cryptographic hashes for Python packages, such as a hash-locked requirements file installed with: ```bash pip install --require-hashes -r requirements.lock ``` 4. Keep the documented installation command consistent with the locked dependency set. 5. Document the trusted package index explicitly and prevent unintended fallback to untrusted or internal indexes. 6. Pin the Playwright version and associated Chromium revision as a tested pair. 7. Verify browser artifacts using integrity controls supplied by the distribution mechanism, or distribute them through a controlled and verified artifact repository. 8. Use automated dependency scanning and a controlled update process in which version changes are reviewed and tested before the lock file is updated. 9. Install dependencies inside an isolated, least-privileged virtual environment or container rather than into a privileged or shared interpreter. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill explicitly instructs users to set library usernames and passwords in environment variables and advertises automated institutional authentication, but provides no guidance on secure credential handling, storage, scope, or privacy implications. In a skill designed to automate access to third-party academic platforms, this increases the risk of credential exposure, unauthorized reuse, and misuse of institutional accounts.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The module docstring says this is a 'Library Downloader' for downloading academic books and articles, and the CLI description repeats that intent. In practice, the main implemented path in download_book only queries Crossref for a DOI and returns a status of 'requires_manual_download' rather than downloading content, while the DOI and URL paths also do not perform downloads.

External Transmission

Medium
Category
Data Exfiltration
Content
# Try Crossref API
    query = f"{title} {author}".strip()
    url = f"https://api.crossref.org/works?query={quote(query)}&rows=1"
    
    try:
        response = requests.get(url, timeout=10)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The function signature includes an output_path parameter and the docstring states 'Download using Playwright', implying a file download side effect. However, the embedded script merely visits the page, locates an anchor ending in .pdf, prints its href, and the Python wrapper returns stdout; output_path is never used and no file is saved.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
temp_path = f.name
    
    try:
        result = subprocess.run(['node', temp_path], capture_output=True, text=True)
        return result.stdout.strip()
    finally:
        os.unlink(temp_path)
Confidence
77% confidence
Finding
The function writes attacker-controlled URL data into a temporary JavaScript file and then executes it with Node. Because the URL is interpolated directly into a single-quoted JS string without escaping, a crafted URL containing quotes or script syntax can break out of the string and trigger arbitrary JavaScript execution in the spawned Node process.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The skill promotes automated downloading, format conversion, and batch processing, which clearly implies creating and modifying local files, but it does not warn users about write behavior, output locations, disk usage, or possible overwrites. While not inherently malicious, the lack of disclosure can lead to unintended local data changes and unsafe execution in sensitive environments.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The script constructs a Crossref API query from the user's title and author and transmits it over the network. Although errors are printed, there is no user-facing warning before this data transmission and the function docstring does not disclose that user input will be sent to a third-party service.

Static analysis

No suspicious patterns detected.