T09 · Insecure Skill Coding Practices
- Location
scripts/order_manager.py:21- Finding
Arbitrary File Overwrite Through Unvalidated Persisted Order IDs
- Content
View full analysis
Vulnerability Details
File Location:
scripts/order_manager.py, lines 12–24 and 87–98
Vulnerability Type: Path traversal and arbitrary file overwrite
Risk Level: HighVulnerable Code
python def _load_all(self): orders = [] for f in os.listdir(DATA_DIR): if f.endswith(".json"): with open(os.path.join(DATA_DIR, f)) as fp: orders.append(json.load(fp)) return orders def _save(self, order): path = os.path.join(DATA_DIR, f"{order['id']}.json") with open(path, "w") as f: json.dump(order, f, indent=2)The untrusted persisted identifier reaches
_save()through the confirmation operation:python elif "--confirm" in args: idx = args.index("--confirm") oid = args[idx+1] if idx+1 < len(args) else "" if not oid: print("Please specify an order ID") return found = None for o in mgr.orders: if o['id'] == oid: found = o break if found: found['status'] = 'paid' found['paid_at'] = datetime.now().isoformat() mgr._save(found)Technical Analysis
_load_all()deserializes every JSON file in the order-data directory without validating its schema or the value of theidproperty._save()then uses that persisted value directly as part of a filesystem path.An identifier containing parent-directory components, such as
../../target, causes the resulting path to escapeDATA_DIR. An absolute identifier can causeos.path.join()to discardDATA_DIRentirely. The.jsonsuffix limits the destination filename but does not prevent writing outside the intended directory.This is a trust-boundary violation: data loaded from mutable persistent storage is treated as a safe filename. The
--confirmoperation provides a reachable path from the malicious record to the unsafe write.Attack Path
...[truncated 1189 chars]
- Remediation
View remediation
Remediation Suggestions
- Validate every loaded order against a strict schema before using it.
- Require identifiers to match the generated format, for example
^[0-9a-f]{12}$. - Do not use a persisted identifier as an unrestricted filesystem path component.
- Resolve the final destination with
os.path.realpath()orpathlib.Path.resolve()and verify that its parent is exactly the expected order directory. - Reject absolute paths, path separators, parent-directory components, and malformed identifiers.
- Consider deriving the storage path from a separately validated identifier rather than trusting the
idproperty inside the file. - Refuse to follow symbolic links when opening the destination.
- Use atomic writes through a securely created temporary file in
DATA_DIR, followed byos.replace(). - Apply the same validation when loading, confirming, listing, and reporting orders.
