T09 · Insecure Skill Coding Practices
- Location
SKILL.md:276- Finding
Path Traversal in Save and Load Slot Handling
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 276–287
Vulnerability Type: Path traversal through an unvalidated save-slot identifier
Risk Level: Mediumpython def save_game(game_state, slot="auto"): """保存游戏进度""" save_dir = Path("~/.text-game/saves").expanduser() save_dir.mkdir(parents=True, exist_ok=True) save_file = save_dir / f"{slot}.json" save_file.write_text(json.dumps(game_state, ensure_ascii=False, indent=2)) # . def load_game(slot="auto"): """读取游戏进度""" save_file = Path("~/.text-game/saves").expanduser() / f"{slot}.json" if save_file.exists(): return json.loads(save_file.read_text()) return NoneTechnical Analysis
The
slotvalue is interpolated directly into a filesystem path without validation or canonical containment checking. Python'spathlibpath-composition behavior permits traversal components such as../. An absolute path supplied as the right-hand operand can also replace the intended base path.Consequently, the resulting
save_fileis not guaranteed to remain beneath~/.text-game/saves. The.jsonsuffix limits target selection to paths ending in that suffix, but it does not prevent access to JSON files elsewhere on the filesystem.This is an insecure coding pattern presented by the skill for agents or users to implement. Exploitability depends on an attacker being able to control the
slotargument in an implementation based on this example.Attack Path
- A victim implements or executes the documented
save_gameorload_gamelogic. - The attacker supplies a crafted slot identifier containing traversal components, such as
../../other/config. - The application constructs a path equivalent to
~/.text-game/saves/../../other/config.json. - No normalization or containment validation rejects the escaped path.
- For
load_game, the process reads and parses the external JSON file if it ...[truncated 713 chars]
- A victim implements or executes the documented
- Remediation
View remediation
Remediation Suggestions
- Restrict slot identifiers to a conservative allowlist, for example
^[A-Za-z0-9_-]{1,64}$. - Resolve both the save directory and candidate file path, then verify that the candidate remains within the resolved save directory.
- Reject absolute paths, path separators,
.components, and..components. - Use explicit UTF-8 encoding for file operations.
- Create save files with restrictive permissions where the operating system supports doing so.
- Avoid overwriting existing files unintentionally; use exclusive creation or atomic replacement where appropriate.
Example hardened implementation:
python import json import re from pathlib import Path SLOT_PATTERN = re.compile(r"^[A-Za-z0-9_-]{1,64}$") def get_save_file(slot="auto"): if not isinstance(slot, str) or not SLOT_PATTERN.fullmatch(slot): raise ValueError("Invalid save-slot identifier") save_dir = Path("~/.text-game/saves").expanduser().resolve() save_dir.mkdir(parents=True, exist_ok=True) save_file = (save_dir / f"{slot}.json").resolve() if not save_file.is_relative_to(save_dir): raise ValueError("Save path escapes the save directory") return save_file def save_game(game_state, slot="auto"): save_file = get_save_file(slot) save_file.write_text( json.dumps(game_state, ensure_ascii=False, indent=2), encoding="utf-8", ) def load_game(slot="auto"): save_file = get_save_file(slot) if save_file.is_file(): return json.loads(save_file.read_text(encoding="utf-8")) return None- Restrict slot identifiers to a conservative allowlist, for example
