T09 · Insecure Skill Coding Practices
- Location
scripts/build_static_reader.py:117- Finding
Arbitrary Recursive Directory Deletion Through User-Controlled Output Path
- Content
View full analysis
Vulnerability Details
File Location:
scripts/build_static_reader.py:117, 129, 141-146
Vulnerability Type: Unrestricted recursive directory deletion
Risk Level: HighVulnerable Code
python parser.add_argument( "--out", default=None, help="Output site directory. Defaults to <analysis_dir>/site.", ) parser.add_argument( "--force", action="store_true", help="Overwrite an existing site directory.", ) # ... out_dir = Path(args.out).resolve() if args.out else analysis_dir / "site" if out_dir.exists() and args.force: shutil.rmtree(out_dir) elif out_dir.exists() and any(out_dir.iterdir()): raise SystemExit( f"output directory exists and is not empty: {out_dir}; use --force" ) copytree_contents(template, out_dir)Technical Analysis
The
--outargument accepts an arbitrary filesystem path. When--forceis supplied, the resolved path is passed directly toshutil.rmtree()without enforcing any safety boundary.The implementation does not verify that:
out_diris a child of the supplied analysis directory;out_diris the expected generatedsitedirectory;out_dirdiffers fromanalysis_dir;- the target was previously created by this application;
- the target is not a filesystem root, home directory, repository root, or another valuable writable directory.
Calling
Path.resolve()canonicalizes the path but does not make deletion safe. It does not restrict the target to an authorized directory.This behavior exceeds the minimum filesystem privileges needed to rebuild a static reader. The application only needs to replace its own generated site files, not recursively delete any directory selected by the caller.
Attack Path
- An attacker, unsafe automation workflow, or misleading instruction controls the command used to invoke the reader builder.
- The attacker supplies a valuable writa ...[truncated 1420 chars]
- Remediation
View remediation
Remediation Suggestions
-
Restrict generated output to a dedicated child directory of
analysis_dir, preferably exactlyanalysis_dir/site. -
If custom output directories must remain supported, verify containment after canonicalization:
python analysis_dir = Path(args.analysis_dir).resolve() out_dir = ( Path(args.out).resolve() if args.out else (analysis_dir / "site").resolve() ) if out_dir == analysis_dir or analysis_dir not in out_dir.parents: raise SystemExit( "output directory must be a child of the analysis directory" ) -
Explicitly reject dangerous targets, including filesystem roots, the current working directory, the user's home directory, and the project or analysis root.
-
Create a generator-specific marker file inside generated sites, such as
.paper-code-reader-generated. -
Permit recursive replacement only when that marker exists and is a regular file inside the selected output directory.
-
Avoid deleting the whole directory where possible. Remove or replace only the known files generated by the reader builder.
-
Check for symbolic-link and path-race conditions immediately before deletion.
-
Add regression tests that ensure
--forcerejects:--outequal toanalysis_dir;- parent directories of
analysis_dir; - arbitrary external directories;
- filesystem roots;
- existing directories without the generator marker.
-
Consider requiring an additional explicit confirmation option for deletion of any non-default generated output path.
-
