T09 · Insecure Skill Coding Practices
- Location
workflow_discoverer.py:25- Finding
Path Traversal Through Unsanitized User Identifier and Storage Path
- Content
View full analysis
Vulnerability Details
File Location:
workflow_discoverer.py, lines 25–46
Vulnerability Type: Path traversal and unsafe filesystem access
Risk Level: MediumVulnerable Code:
python # Storage self.storage_path = storage_path or f"~/.workflow_discoverer/{user_id}.json" self.data = self._load() # Initialize self.data.setdefault('tasks', []) self.data.setdefault('sequences', []) self.data.setdefault('workflows', []) self.data.setdefault('automations', []) def _load(self) -> Dict: """Load data""" path = os.path.expanduser(self.storage_path) if os.path.exists(path): with open(path, 'r') as f: return json.load(f) return {} def _save(self): """Save data""" path = os.path.expanduser(self.storage_path) os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, 'w') as f: json.dump(self.data, f, indent=2)Technical Analysis
The constructor directly interpolates the caller-controlled
user_idinto a filesystem path. Neitheruser_idnor a caller-suppliedstorage_pathis validated, canonicalized, or checked against an approved storage directory.Path components such as
../can therefore escape the intended~/.workflow_discovererdirectory. The resulting path is passed toopen()for both reading and writing. The implementation also follows symbolic links and does not verify that the resolved target is a regular file owned by the expected user.Reading requires the target file to contain valid JSON because
json.load()is used. Writing occurs when methods such asrecord_task(),create_automation(),enable_automation(), ordisable_automation()call_save().Attack Path
- An attacker gains control over the
user_idargument or the optionalstorage_pathargument passed toWorkflowDiscoverer. - The attacker supplies a traversal value such as
../targetforuser_id. - Th ...[truncated 1133 chars]
- An attacker gains control over the
- Remediation
View remediation
Remediation Suggestions
- Do not use raw user identifiers as filenames. Convert them to a fixed safe representation, such as a SHA-256 digest.
- If readable identifiers are required, enforce a strict allowlist such as
^[A-Za-z0-9_-]{1,64}$. - Resolve the base directory and destination with
pathlib.Path.resolve(), then verify that the destination remains beneath the approved base directory. - Reject absolute paths and traversal components in caller-provided
storage_pathvalues, or remove the ability for untrusted callers to select storage paths. - Defend against symbolic-link attacks by rejecting symlink targets and, where supported, opening files with safe flags such as
O_NOFOLLOW. - Write through a securely created temporary file in the same directory and atomically replace the destination.
- Apply restrictive directory and file permissions, such as
0700for the storage directory and0600for data files. - Handle malformed JSON and filesystem errors without exposing unrelated file contents or corrupting existing state.
