Back to skill

Security audit

Astro

Security checks for vulnerabilities and agentic risk

Overview

This Astro helper is mostly purpose-aligned, but one bundled script can create new Markdown files outside the intended content folder if given crafted inputs.

Review before installing. Use the Astro and Cloudflare commands only in a trusted project, prefer pinned/local tool versions for wrangler and astro, and avoid passing untrusted or path-like values to astro-new-post.py. The post-creation helper should be fixed to validate language codes, sanitize slugs, and verify that created files remain inside the intended content directory.

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/astro-new-post.py:22
Finding

Path Traversal Allows Markdown Files to Be Written Outside the Content Directory

Content
View full analysis

Vulnerability Details

File Location: scripts/astro-new-post.py, lines 22–48
Vulnerability Type: Path traversal and arbitrary file creation
Risk Level: High

Vulnerable Code

python
# Create language directory
lang_dir = content_dir / lang
lang_dir.mkdir(parents=True, exist_ok=True)

# Generate slug from title
slug = title.lower().replace(" ", "-").replace("'", "")
for char in [",", ".", "!", "?", ":", ";"]:
    slug = slug.replace(char, "")

# Create filename
filename = f"{slug}.md"
filepath = lang_dir / filename

# Check if file already exists
if filepath.exists():
    print(f"⚠️  File already exists: {filepath}")
    return filepath

# Generate frontmatter
today = datetime.now().strftime("%Y-%m-%d")
tags_str = str(tags) if tags else "[]"

frontmatter = f"""---
title: "{title}"
description: ""
pubDate: {today}
author: "{author}"
lang: "{lang}"
tags: {tags_str}
---

# {title}

Write your content here...
"""

# Write file
filepath.write_text(frontmatter, encoding="utf-8")

Technical Analysis

The script directly uses the user-controlled lang value as a filesystem path component:

python
lang_dir = content_dir / lang

It does not reject absolute paths, .. traversal components, path separators, or other values that resolve outside the configured content directory. With pathlib, an absolute right-hand operand can also replace the preceding base path.

The user-controlled title is converted into a slug using only limited punctuation removal. Path separators and traversal components are not removed or rejected. The resulting slug is then used to construct the destination filename.

Neither the language directory nor the final file path is resolved and checked for containment beneath content_dir. Consequently, a caller able to supply command-line arguments can direct the script to create directories and a new Markdown fi ...[truncated 1801 chars]

Remediation
View remediation

Remediation Suggestions

  • Validate language identifiers against a strict allowlist or conservative locale pattern, for example:

    python
    import re
    
    LOCALE_PATTERN = re.compile(r"^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})?$")
    
    if not LOCALE_PATTERN.fullmatch(lang):
        raise ValueError(f"Invalid language code: {lang}")
    
  • Reject empty language values, absolute paths, path separators, . components, and .. components.

  • Replace the current title transformation with a slug generator that permits only a restricted set of characters, such as lowercase ASCII letters, digits, and hyphens.

  • Resolve both the content root and destination and verify containment before creating directories or writing:

    python
    content_root = content_dir.resolve()
    destination = (content_root / lang / f"{slug}.md").resolve()
    
    if destination.parent != content_root / lang:
        raise ValueError("Destination escapes the expected language directory")
    
    if content_root not in destination.parents:
        raise ValueError("Destination escapes the content directory")
    
  • Create the file using exclusive creation, such as mode x, to avoid race conditions between the existence check and write.

  • If languages are known by the project, prefer an explicit configured allowlist such as {"en", "es", "fr"} rather than accepting arbitrary directory names.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:20
Finding

Unpinned npm and npx Commands Execute Mutable Third-Party Package Versions

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 20, 42, 64, 118, and 196–198
Vulnerability Type: Unpinned third-party dependency execution
Risk Level: Medium

Vulnerable Code

bash
npm create astro@latest my-site -- --template minimal
bash
npm install @astrojs/cloudflare
bash
npx wrangler pages deploy dist
bash
npx astro sync

The command reference repeats mutable npx execution:

markdown
| `npx astro sync` | Generate content collection types |
| `npx wrangler login` | Authenticate with Cloudflare |
| `npx wrangler pages deploy dist` | Deploy to Cloudflare |

Technical Analysis

The documented workflow executes packages without immutable version constraints. In particular:

  • @latest explicitly selects whatever release is current when the command is run.
  • npm install @astrojs/cloudflare resolves a version from the registry at installation time.
  • npx may resolve or install a package dynamically when a suitable local binary is unavailable.
  • No lockfile, integrity value, or exact reviewed version is provided by this project.

Package installation and CLI invocation can execute package code, including lifecycle scripts and command entry points, with the privileges of the invoking user. The referenced package names are consistent with the legitimate Astro and Cloudflare ecosystems, and the audited files do not identify a malicious package or suspicious registry. The risk arises because the effective third-party code can change after the Skill has been reviewed.

Attack Path

  1. A user follows the setup or deployment commands in SKILL.md.
  2. npm or npx queries the configured package registry.
  3. The registry resolves a package version that was not fixed at audit time.
  4. The package is downloaded and installed or invoked.
  5. Its lifecycle scripts or CLI entry point execute with the user's local or CI privileges. 6 ...[truncated 854 chars]
Remediation
View remediation

Remediation Suggestions

  • Pin every directly invoked package to an exact reviewed version rather than using @latest or unconstrained installation.
  • Add required CLIs to project development dependencies with exact versions.
  • Commit a generated lockfile and use npm ci in CI and deployment environments.
  • Invoke locked local binaries through package scripts rather than allowing npx to install missing packages dynamically.
  • Configure npx to avoid implicit installation where supported, for example with --no-install after installing the pinned dependency locally.
  • Enable lockfile integrity verification, dependency review, and automated vulnerability scanning.
  • Review version upgrades explicitly before updating the lockfile.
  • Isolate package installation and build steps in a least-privileged container or CI worker, and expose deployment credentials only to the minimum required step.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (8)

Rp1

Medium
Category
MCP Rug Pull
Confidence
86% confidence
Finding

The skill instructs users to run npx wrangler pages deploy dist without pinning a version. npx may fetch the latest package version at execution time, which creates a supply-chain risk: users could receive unexpected, compromised, or breaking code if the upstream package or dependency chain changes. In a deployment-oriented skill, this is more dangerous because it directly affects infrastructure publishing workflows and may involve authenticated Cloudflare sessions.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
78% confidence
Finding

The instruction to run npx astro sync uses an unpinned package invocation. If astro is not already installed locally or resolution falls back to a fetched version, users may execute unreviewed code from the registry, introducing supply-chain and reproducibility risks. The surrounding context is a developer setup guide, so readers are likely to copy-paste commands, which increases practical exposure.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
78% confidence
Finding

npx astro sync is again referenced without a pinned version in the commands table. Unpinned npx usage can execute newly published package code and makes builds less reproducible, which is a recognizable supply-chain weakness even in documentation. Because this command is presented as a standard workflow step, users may trust and reuse it broadly.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
87% confidence
Finding

The command npx wrangler login invokes a package without version pinning before an authentication step. This is particularly sensitive because users may run it while preparing to grant Cloudflare access; a malicious or compromised upstream package could abuse that trust context, steal tokens, or alter login flows. The cloud-deployment context makes this more dangerous than a generic local tooling example.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
87% confidence
Finding

The command npx wrangler pages deploy dist is unpinned and used in a production deployment context. An unexpected package version or compromised release could execute arbitrary code during deployment, tamper with build artifacts, or misuse Cloudflare credentials. Since the skill is explicitly about publishing sites to Cloudflare, the operational context increases the potential blast radius.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
78% confidence
Finding

The troubleshooting guidance again recommends npx astro sync without version pinning. Although common in tutorials, this still exposes users to registry-time package changes and undermines deterministic setup, especially when users are following remediation steps during errors and may be less cautious. The risk is moderate rather than high because the command is a normal developer tool invocation, not an obvious credential-handling action.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
78% confidence
Finding

This repeated npx astro sync example is an unpinned package execution and carries the same supply-chain and reproducibility risk as the other Astro references. Repetition in the skill increases the chance that users will copy one of these commands directly, normalizing unsafe invocation patterns. The context is not overtly malicious, but it does encourage risky operational hygiene.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The CLI sets --default-lang to en by default, which encodes a language preference directly into the tool's behavior. The file does not document a user opt-in flow or a region-specific reason for privileging English, so this can be read as a locale policy issue under the stated rule.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.