T09 · Insecure Skill Coding Practices
- Location
scripts/stage_persistence_utils.py:62- Finding
Path Traversal Enables Arbitrary File Writes Outside the Project
- Content
View full analysis
bool: return stage == 'character-system' and relpath.startswith('characters/') and relpath.endswith('.md') def validate_artifact_updates(stage: str, artifact_updates: dict[str, str], substage: str | None = None) -> dict[str, str]: if not isinstance(artifact_updates, dict): raise ValueError('artifactUpdates must be an object') allowed = document_stage_targets(stage, substage) normalized = {} for relpath, text in artifact_updates.items(): if not isinstance(relpath, str) or not relpath.strip(): raise ValueError('artifact path must be a non-empty string') if relpath not in allowed and not allow_character_file(stage, relpath): raise ValueError(f'out-of-scope artifact for {stage}: {relpath}') if not isinstance(text, str) or not text.strip(): raise ValueError(f'artifact content cannot be empty: {relpath}') normalized[relpath] = text return normalized ``` The validated value is subsequently used as a filesystem path without containment verification: ```python validated = validate_artifact_updates(args.stage, payload.get('artifactUpdates', {}), substage) for relpath, text in validated.items(): path = project / relpath path.parent.mkdir(parents=True, exist_ok=True) path.write_text(text, encoding='utf-8') ``` ### Technical Analysis Character artifact paths are approved using only textual prefix and suffix checks. A value such as `characters/../../outside.md` starts with `characters/` and ends with `.md`, so it passes validation. When the path is joined to the project directory, the operating system resolves the `. ...[truncated 1584 chars]- Remediation
View remediation
