T09 · Insecure Skill Coding Practices
- Location
scripts/agent_memory_optimizer.py:55- Finding
Unconditional Destination Replacement Can Destroy Archived Memory
- Content
View full analysis
Vulnerability Details
File Location:
scripts/agent_memory_optimizer.py, lines 55-64
Vulnerability Type: Unsafe file overwrite and data loss
Risk Level: Mediumpython for src, month_folder in candidates: dst_dir = archive_root / month_folder dst = dst_dir / src.name moved.append({"from": str(src), "to": str(dst)}) if dry_run: continue dst_dir.mkdir(parents=True, exist_ok=True) if dst.exists(): dst.unlink() shutil.move(str(src), str(dst))Technical Analysis
When an archive destination already exists, the script unconditionally deletes it with
dst.unlink()and then moves the source file into its place. It does not verify whether the source and destination contain identical data, create a backup, request confirmation, or require an explicit overwrite option.Consequently, two distinct memory notes with the same filename cannot coexist safely. The behavior also contradicts the documentation stating that the operation is safe to repeat and idempotent: rerunning the tool after a same-name source file is created can silently replace previously archived content.
The existence check and subsequent operations are also separate filesystem actions. A concurrent process could change the destination between the check, deletion, and move. The central confirmed issue, however, is the intentional and unconditional removal of any existing destination.
Attack Path
- An existing note is stored at
memory/archive/YYYY-MM/<filename>.md. - A distinct date-prefixed file with the same filename is placed in the top-level
memorydirectory. - Its filename represents a month older than the selected cutoff, causing
iter_candidates()to select it. - The user runs the script without
--dry-run. - The script detects the existing destination and deletes it using
dst.unlink(). - The new source is moved to that path, permanently replacing the p ...[truncated 825 chars]
- An existing note is stored at
- Remediation
View remediation
Remediation Suggestions
- Refuse destination collisions by default and report each skipped file as a conflict.
- Add an explicit
--overwriteoption if replacement is necessary. Clearly warn that it is destructive. - Compare cryptographic hashes before treating an existing destination as already archived. If the files are identical, safely remove or skip the duplicate source; if they differ, preserve both files or report a conflict.
- Preserve conflicting files using a deterministic unique name, such as a timestamp or content-hash suffix.
- Avoid a check-then-delete sequence. Use an atomic no-replace filesystem operation where available, such as
renameat2(..., RENAME_NOREPLACE)on supported Linux systems. - If a portable copy-based fallback is used, create the destination exclusively, verify that the copy completed successfully, preserve relevant metadata, and only then remove the source.
- Record separate result states such as
moved,skipped_identical, andcollisionso callers can detect incomplete archival operations. - Add tests covering an absent destination, an identical destination, a different destination with the same name, concurrent destination changes, and explicit overwrite behavior.
