Back to skill

Security audit

A Share Metrics Card

Security checks for vulnerabilities and agentic risk

Overview

This stock-card skill is purpose-aligned overall, but needs review because its script can overwrite arbitrary local files if given a crafted output path or stock symbol.

Install only if you are comfortable with a skill that writes local files. Use a normal six-digit stock symbol and keep output paths under notes/stocks/cards; review or patch the script to validate symbols, reject path traversal, and avoid overwriting existing files without confirmation.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/metrics_card.py:27
Finding
Unrestricted Output Path Allows Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/metrics_card.py`, lines 27–34 and 192 **Vulnerability Type**: Improper path validation and unrestricted file write **Risk Level**: Medium ### Vulnerable Code ```python ap.add_argument("--symbol", required=True, help="6-digit A-share code, e.g., 600406") ap.add_argument("--name", default="", help="Chinese name (optional)") ap.add_argument("--out", default="", help="Output markdown path") args = ap.parse_args() symbol = args.symbol.strip() name = args.name.strip() out_path = Path(args.out) if args.out else Path("notes/stocks/cards") / f"{symbol}.md" out_path.parent.mkdir(parents=True, exist_ok=True) ``` The resulting path is subsequently written without any containment or overwrite checks: ```python out_path.write_text("\n".join(lines) + "\n", encoding="utf-8") ``` ### Technical Analysis The documentation states that the stock symbol should be validated, but the implementation only removes surrounding whitespace. It does not enforce the documented six-digit A-share symbol format. When `--out` is supplied, the value is converted directly to a `Path`. Absolute paths, parent-directory components such as `../`, symbolic-link targets, and paths outside the intended `notes/stocks/cards` directory are all accepted. When `--out` is omitted, the unvalidated `symbol` is incorporated into the default output path. A symbol containing path separators or traversal components can therefore alter the destination. The call to `mkdir(parents=True, exist_ok=True)` may also create attacker-selected directory trees. Finally, `Path.write_text()` overwrites an existing destination by default. There is no canonical-path containment check, symbolic-link defense, confirmation requirement, or protection against replacing an existing file. ### Attack Path 1. An attacker or untrusted caller provides a crafted output argument, for example: ```bash python scripts/metrics_card.py --symbol 600406 --out /tmp/target- ...[truncated 1347 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce the documented stock-symbol format before using it in a path: ```python import re if not re.fullmatch(r"[036]\d{5}", symbol): ap.error("--symbol must be a six-digit A-share code beginning with 0, 3, or 6") ``` 2. Restrict output files to an explicitly approved root directory: ```python output_root = Path("notes/stocks/cards").resolve() candidate = ( Path(args.out) if args.out else output_root / f"{symbol}.md" ) candidate = candidate.resolve() try: candidate.relative_to(output_root) except ValueError: ap.error("--out must remain inside notes/stocks/cards") ``` 3. Reject absolute paths and parent-directory components before resolution if callers are not intended to select arbitrary destinations. 4. Define a clear overwrite policy. Refuse to replace existing files by default, or require an explicit trusted `--force` option: ```python if candidate.exists() and not args.force: ap.error("Output file already exists; use --force to replace it") ``` 5. If the execution environment may contain attacker-controlled symbolic links, open the destination using platform-supported no-follow and exclusive-creation controls rather than relying solely on a pre-write existence check. 6. Run the Skill with minimal filesystem permissions so that only the designated card-output directory is writable. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill explicitly plans to use network access to fetch stock data and to write a Markdown file, but it does not declare any tool restrictions or permissions. This creates an overbroad execution profile where an agent runtime may grant more capabilities than are necessary or fail to surface the risk to users, increasing the chance of unintended file writes or unreviewed external data access.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The skill says it will write a generated Markdown card to outputPath by default, but it does not clearly warn the user that invoking the skill modifies local files. Even though the default path appears intended for notes, silent file creation or overwrite can surprise users and becomes riskier if outputPath is user-controlled or if existing files are replaced.

Static analysis

No suspicious patterns detected.