T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:66- Finding
Unrestricted Output Path Allows Arbitrary File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.py, lines 66–70 and 94–96
Vulnerability Type: Unrestricted file write / path traversal
Risk Level: MediumVulnerable Code
python parser.add_argument( "--output", "-o", type=str, help="Output JSON file path (optional, prints to stdout if not specified)" )python if args.output: with open(args.output, 'w', encoding='utf-8') as f: f.write(output) print(f"Table saved to: {args.output}")Technical Analysis
The caller-controlled
--outputvalue is passed directly toopen()in write mode. The implementation does not reject absolute paths or parent-directory traversal, canonicalize the path against an authorized workspace, check for symbolic links, or require exclusive file creation.Consequently, the process can create or truncate any file writable under its operating-system privileges. The
wmode truncates an existing target before writing the generated JSON. Although the attacker cannot supply arbitrary file contents through this interface, destructive overwrite and configuration corruption remain possible.This behavior also conflicts with the security controls identified in
SKILL.md, which state that output should be restricted to the workspace and paths should be validated against../traversal.Attack Path
- An attacker gains influence over the skill's command-line arguments.
- The attacker supplies an absolute path or traversal path, such as
--output ../../target-file. - Python resolves the path without any application-level containment check.
open(args.output, 'w')creates the target or truncates an existing writable file.- The generated JSON replaces the previous contents, potentially corrupting data or configuration.
Impact Assessment
Exploitation is limited to the filesystem permissions of the account running the skill and does not itself provide ...[truncated 348 chars]
- Remediation
View remediation
Remediation Suggestions
- Define a dedicated, trusted output directory beneath the project workspace.
- Reject absolute paths and resolve the requested path with
pathlib.Path.resolve(). - Verify that the resolved target remains beneath the trusted output directory using
Path.relative_to()or an equivalent containment check. - Reject parent-directory traversal and symbolic-link targets.
- Use exclusive creation mode (
x) where overwriting is unnecessary, or require explicit authorization before replacing an existing file. - Create output directories with restrictive permissions and run the skill under a least-privileged account.
- Return a sanitized error when path validation fails.
Example containment pattern:
python from pathlib import Path output_root = (Path.cwd() / "output").resolve() output_root.mkdir(parents=True, exist_ok=True) requested = Path(args.output) if requested.is_absolute(): raise ValueError("Absolute output paths are not allowed") target = (output_root / requested).resolve() try: target.relative_to(output_root) except ValueError: raise ValueError("Output path must remain within the output directory") if target.is_symlink(): raise ValueError("Symbolic-link output targets are not allowed") with target.open("x", encoding="utf-8") as f: f.write(output)
