T09 · Insecure Skill Coding Practices
- Location
scripts/autodream_cycle.py:507- Finding
Dry-Run Mode Destructively Overwrites Persistent Memory Without Backup or Locking
- Content
View full analysis
Vulnerability Details
File Location:
scripts/autodream_cycle.py, lines 507-509 and 678-683
Vulnerability Type: Dry-run violation, unsafe persistent-file replacement, and missing concurrency safeguards
Risk Level: MediumVulnerable Code
python # scripts/autodream_cycle.py:507-509 # Write MEMORY.md memory_md.parent.mkdir(parents=True, exist_ok=True) memory_md.write_text("\n".join(lines), encoding="utf-8")python # scripts/autodream_cycle.py:678-683 # Phase 4: Prune and Index prune_result = phase4_prune_and_index(workspace, consolidation) # Generate report if not args.dry_run: report_path = write_report( workspace, consolidation, orientation, prune_result )The command-line option is explicitly described as non-writing:
python # scripts/autodream_cycle.py:639-640 parser.add_argument( "--dry-run", action="store_true", help="Trial run without writing files" )Technical Analysis
The dry-run condition is checked only after
phase4_prune_and_index()has returned. That phase unconditionally replaces the workspace'sMEMORY.md, so--dry-runsuppresses report and state files but does not suppress the primary persistent-memory modification.The replacement is also performed directly with
Path.write_text(). It does not:- Create the backup promised by the Skill documentation and configuration.
- Acquire the lock promised by the Skill documentation.
- Write to a temporary file and atomically replace the destination.
- Preserve entries found only in the existing
MEMORY.md.
The consolidation input is collected from
memory/*.mdandmemory/topics/*.md, while the existingMEMORY.mdis only read to obtain its line count. Consequently, entries that exist exclusively inMEMORY.mdcan disappear when the replacement index is generated.The issue does not require code execution, elevated privileges, or an external attacker. It is triggered through a documented command and affects any ` ...[truncated 1486 chars]
- Remediation
View remediation
Remediation Suggestions
-
Enforce dry-run mode before every mutation:
python if not dry_run: memory_md.write_text(output, encoding="utf-8")Prefer passing a
dry_runargument into every phase that may modify the filesystem. -
Separate computation from persistence:
- Build the proposed
MEMORY.mdentirely in memory. - Return the proposed content and change summary.
- Perform all writes in one guarded commit stage only when dry-run is disabled.
- Build the proposed
-
Back up the original file before replacement:
- Create
memory/autodream/backup/. - Use a timestamped, collision-resistant backup filename.
- Verify that the backup was written successfully before modifying the original.
- Create
-
Use atomic replacement:
- Write the complete output to a temporary file in the destination directory.
- Flush and synchronize it where appropriate.
- Replace
MEMORY.mdwithos.replace()only after successful completion. - Clean up temporary files on failure.
-
Implement exclusive locking:
- Acquire a workspace-specific lock before reading and modifying persistent memory.
- Reject or safely wait for concurrent runs.
- Release the lock through a
finallyblock.
-
Preserve index-only content:
- Include existing
MEMORY.mdentries in consolidation, or - Explicitly identify proposed removals and require confirmation before deleting them.
- Include existing
-
Add regression tests that verify:
--dry-runproduces no filesystem changes.- A failed write leaves the original file intact.
- Concurrent runs cannot overwrite one another.
- Backups are created before replacement.
- Entries found only in
MEMORY.mdare preserved or explicitly reported.
-
