T09 · Insecure Skill Coding Practices
- Location
forge.py:23- Finding
Persistent State and SOUL.md Backups Are Written Outside the Skill Directory
- Content
View full analysis
Vulnerability Details
File Location:
observe.py:20-23,reflect.py:18-21, andforge.py:23-27
Vulnerability Type: Incorrect base-directory resolution and cross-project state exposure
Risk Level: MediumVulnerable Code
observe.py:20-23:python SKILL_DIR = Path(__file__).parent.parent MEMORY_DIR = SKILL_DIR / "memory" OBSERVATIONS_FILE = MEMORY_DIR / "observations.json" MEMORY_DIR.mkdir(exist_ok=True)reflect.py:18-21:python SKILL_DIR = Path(__file__).parent.parent MEMORY_DIR = SKILL_DIR / "memory" OBSERVATIONS_FILE = MEMORY_DIR / "observations.json"forge.py:23-27:python SKILL_DIR = Path(__file__).parent.parent MEMORY_DIR = SKILL_DIR / "memory" OBSERVATIONS_FILE = MEMORY_DIR / "observations.json" BACKUPS_DIR = MEMORY_DIR / "backups" BACKUPS_DIR.mkdir(parents=True, exist_ok=True)Technical Analysis
The three Python files are located directly in the project root rather than in the documented
scripts/subdirectory. As a result,Path(__file__).parent.parentresolves to the parent of the project directory, not the project directory itself.The effective paths therefore become:
text <project-parent>/memory/observations.json <project-parent>/memory/backups/instead of the documented locations:
text <project>/memory/observations.json <project>/memory/backups/This creates an unintended shared-state boundary. Other projects or local users with write access to the project parent can create or alter the observation file consumed by
reflect.pyandforge.py. Likewise,observe.pystores session-derived behavioral data outside the expected Skill directory, andforge.pycopies SOUL.md into an externally located backup directory.The use of
mkdir()at module scope also means importingobserve.pyorforge.pycan create directories outside the Skill without invoking their command-line entry points.Attack Path
- An attacker with local write access to the parent directory c ...[truncated 1647 chars]
- Remediation
View remediation
Remediation Suggestions
- Resolve the Skill directory according to the actual package layout:
python SKILL_DIR = Path(__file__).resolve().parent MEMORY_DIR = SKILL_DIR / "memory"Alternatively, move the Python files into the documented
scripts/directory and retain the parent traversal only after verifying the resolved destination.- Validate that all state paths remain beneath the intended Skill directory:
python SKILL_DIR = Path(__file__).resolve().parent MEMORY_DIR = (SKILL_DIR / "memory").resolve() if SKILL_DIR not in MEMORY_DIR.parents: raise RuntimeError("Memory directory escapes the skill directory")-
Remove import-time filesystem mutations. Create
memory/andbackups/only inside explicit runtime initialization functions. -
Create state directories and files with restrictive permissions appropriate to the platform, because observations and SOUL.md backups may contain sensitive behavioral or identity information.
-
Validate the observations JSON schema before using it. Require expected types and bounded values for fields such as
session_count,tone_history,hedging, andvocabulary. -
Write
observations.jsonatomically by creating a restricted temporary file in the same directory, flushing it, and replacing the destination. This reduces corruption and race-condition risks. -
Add tests asserting that
OBSERVATIONS_FILEandBACKUPS_DIRresolve beneath the project root for the distributed package layout. -
Update the documentation only after implementation paths and the published file structure are consistent.
