T09 · Insecure Skill Coding Practices
- Location
scripts/execute.py:38- Finding
Manifest-Controlled Source Paths Permit Out-of-Scope File Moves
- Content
View full analysis
Vulnerability Details
File Location:
scripts/execute.py, lines 38-42 and 60-84
Vulnerability Type: Untrusted path usage and missing directory confinement
Risk Level: HighVulnerable Code
python # Load manifest (prefer verified, fallback to auto) manifest_path = MANIFEST_IN if os.path.exists(MANIFEST_IN) else MANIFEST_FALLBACK if not os.path.exists(manifest_path): print('[ERROR] No manifest found. Run extract.py first.') return with open(manifest_path, 'r', encoding='utf-8') as f: manifest = json.load(f) ready = [m for m in manifest if m.get('status') == 'ready'] skipped = [m for m in manifest if m.get('status') != 'ready'] print(f'\n[DRY RUN] {len(ready)} files to rename, {len(skipped)} skipped\n') # Assign duplicate counters dup_counters = {} for m in ready: if m.get('is_duplicate') and m.get('duplicate_group'): grp = m['duplicate_group'] dup_counters[grp] = dup_counters.get(grp, 0) + 1 errors = [] if not dry_run: backup_dir = os.path.join(folder, f'_backup_{datetime.now().strftime("%Y%m%d_%H%M%S")}') os.makedirs(backup_dir, exist_ok=True) print(f'[BACKUP] {backup_dir}\n') for m in ready: new_name = build_filename(m, counter=dup_counters.get(m['duplicate_group']) if m.get('is_duplicate') else None) dst = os.path.join(folder, new_name) # Handle name collisions base, ext = os.path.splitext(new_name) counter = 1 while os.path.exists(dst) and os.path.abspath(dst) != os.path.abspath(m['filepath']): new_name = f'{base} ({counter}){ext}' dst = os.path.join(folder, new_name) counter += 1 if dry_run: print(f' {m["filename"]}') print(f' -> {new_name}') print() else: try: if os.path.exists(m['filepath']): shutil.copy2(m['filepath'], os.path.join(backup_dir, m['filename'])) shut ...[truncated 2770 chars]- Remediation
View remediation
Remediation Suggestions
-
Do not trust or use an absolute
filepathstored in the manifest during execution. Derive the source from the selected folder and validated basename:python root = os.path.realpath(folder) filename = m["filename"] if filename != os.path.basename(filename): raise ValueError("Manifest filename must not contain path components") src = os.path.realpath(os.path.join(root, filename)) if os.path.commonpath([root, src]) != root: raise ValueError("Source path escapes the selected folder") -
Require the source to be a regular file and to have a
.pdfsuffix:python if not os.path.isfile(src) or not filename.lower().endswith(".pdf"): raise ValueError("Source must be a regular PDF file") -
Apply equivalent confinement validation to the destination and backup paths.
-
Reject symlinks or explicitly define and enforce a safe symlink policy.
-
Store only relative filenames in manifests, not absolute source paths.
-
Record the canonical extraction root in the manifest and require it to match the execution root.
-
Do not distribute generated
manifest.jsonormanifest_verified.jsonfiles. Generate them for each folder at runtime. -
Abort execution if the manifest is stale, references missing files, contains duplicate source entries, or was not generated for the requested folder.
-
Add automated tests covering absolute paths,
..traversal, path separators in filenames, symlinks, stale manifests, and sources outside the selected root.
-
