T09 · Insecure Skill Coding Practices
- Location
persistence/vault.py:90- Finding
Path Traversal and Record Overwrite in File-Based Vault
- Content
View full analysis
Vulnerability Details
File Location:
persistence/vault.py:90-132
Vulnerability Type: Path traversal, arbitrary JSON file access, and insecure file overwrite
Risk Level: HighVulnerable Code
python class FileVault(VaultBackend): """File-based vault using JSON (simple persistence)""" def __init__(self, path: str = "./nexuseternal_vault"): self.path = path os.makedirs(path, exist_ok=True) def _record_path(self, hash_key: str) -> str: return os.path.join(self.path, f"{hash_key}.json") def store(self, record: VaultRecord) -> bool: try: data = { "hash": record.hash, "glyph": record.glyph, "value": record.value, "delta": record.delta, "timestamp": record.timestamp, "layer_state": record.layer_state, "relationships": record.relationships, "equilibrium_delta": record.equilibrium_delta, } with open(self._record_path(record.hash), 'w') as f: json.dump(data, f, indent=2, default=str) return True except Exception as e: print(f"Vault store error: {e}") return False def retrieve(self, hash_key: str) -> Optional[VaultRecord]: try: with open(self._record_path(hash_key), 'r') as f: data = json.load(f) return VaultRecord(**data) except FileNotFoundError: return None except Exception as e: print(f"Vault retrieve error: {e}") return None def exists(self, hash_key: str) -> bool: return os.path.exists(self._record_path(hash_key)) def list_all(self) -> List[str]: files = os.listdir(self.path) return [f.replace('.json', '') for f i ...[truncated 2565 chars]- Remediation
View remediation
Remediation Suggestions
- Accept only internally generated hash keys and validate every key against an exact allowlist pattern, such as
^[0-9a-f]{16}$. - Resolve both the vault root and candidate file path with
pathlib.Path.resolve(), then verify that the candidate is contained under the resolved vault root. - Reject path separators, traversal components, absolute paths, null bytes, and keys of unexpected length.
- Make backend write methods private or require callers to submit record data without supplying the storage key.
- Reject duplicate hashes or use exclusive creation mode (
x) when immutability is required. - Write through a securely created temporary file in the same directory, flush and synchronize it, and atomically rename it into place.
- Create the vault directory and files with restrictive permissions appropriate to the stored data.
- Add tests for
../, absolute paths, nested traversal, symlink escapes, duplicate records, and malformed keys.
Example containment strategy:
python import re from pathlib import Path HASH_PATTERN = re.compile(r"^[0-9a-f]{16}$") def _record_path(self, hash_key: str) -> Path: if not HASH_PATTERN.fullmatch(hash_key): raise ValueError("Invalid vault hash") root = Path(self.path).resolve() candidate = (root / f"{hash_key}.json").resolve() if candidate.parent != root: raise ValueError("Vault path escapes storage directory") return candidate- Accept only internally generated hash keys and validate every key against an exact allowlist pattern, such as
