T09 · Insecure Skill Coding Practices
- Location
scripts/init_change_archive.py:82- Finding
Predictable Archive Naming Allows Silent Overwrite of Existing Audit Evidence
- Content
View full analysis
Vulnerability Details
File Location:
scripts/init_change_archive.py, lines 82-89 and 140-154
Vulnerability Type: Predictable file naming and unsafe overwrite
Risk Level: MediumVulnerable Code
python ts = datetime.now().astimezone() if not args.timestamp else datetime.fromisoformat(args.timestamp) date_part = ts.strftime('%Y-%m-%d') time_part = ts.strftime('%H%M') slug = slugify(args.change_name) archive_root, persistent, warnings, source = resolve_archive_root(args.archive_root, workspace) change_dir = archive_root / 'backups' / 'system-changes' / date_part / f'{time_part}-{slug}'python write_text(pre / 'README.md', readme) write_text(pre / 'meta.json', json.dumps(meta, indent=2, ensure_ascii=False) + '\n') write_text(pre / 'plan.md', plan) write_text(post / 'restart-result.md', restart_result) write_text(post / 'verify.md', verify) copied = [] missing = [] if args.copy_before: for f in args.files: src = Path(f).expanduser() if src.exists() and src.is_file(): dest = rel_dest(pre / 'backup', src.resolve()) dest.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(src, dest) copied.append({'src': str(src.resolve()), 'dest': str(dest)})Technical Analysis
The archive directory name is derived from a timestamp with only minute-level precision and a deterministic slug. Two initializations using the same change name during the same minute therefore select the same archive directory.
Directory creation elsewhere in the script uses
exist_ok=True, whilePath.write_text()andshutil.copy2()overwrite existing destination files. The script neither checks whether the calculated change directory already exists nor creates it atomically with exclusive semantics.Consequently, a repeated, concurrent, or deliberately colliding invocation can silently replace:
- Archive metadata
- Execution and rollback plans
- Restart and verification records
- Before- ...[truncated 1443 chars]
- Remediation
View remediation
Remediation Suggestions
- Add seconds, microseconds, and a cryptographically random or UUID component to every archive directory name.
- Create the final change directory atomically with
mkdir(parents=False, exist_ok=False)after creating only its parent directories. - Abort with a clear error if an archive path already exists; never reuse it implicitly.
- Open fixed archive files using exclusive creation mode, such as
open("x", encoding="utf-8"), where replacement is not explicitly intended. - Before copying backup files, verify that the destination does not exist. Treat collisions as errors rather than overwriting them.
- If deliberate archive updates are required, implement a separate explicit update mode with revision history and integrity logging.
- Consider storing checksums for archived before-state files and metadata so later integrity checks can detect unauthorized replacement.
