T09 · Insecure Skill Coding Practices
- Location
scripts/invoice_tracker.py:66- Finding
Invoice Ledger Created Without Restrictive File Permissions
- Content
View full analysis
Vulnerability Details
File Location:
scripts/invoice_tracker.py, lines 66–70
Vulnerability Type: Sensitive data stored with potentially excessive local read permissions
Risk Level: Mediumpython def save_invoices(invoices): """Save invoices to disk with schema versioning.""" DATA_PATH.parent.mkdir(parents=True, exist_ok=True) wrapped = {"_schema_version": SCHEMA_VERSION, "invoices": invoices} with open(DATA_PATH, "w") as f: json.dump(wrapped, f, indent=2)Technical Analysis
The invoice ledger contains potentially sensitive business and personal information, including payer names, invoice amounts, currencies, due dates, payment status, notes, and reminder history. The application creates its data directory and invoice file without explicitly setting restrictive permissions.
File permissions therefore depend on the process umask. Under a common
022umask,invoices.jsonmay be created with mode0644, making it readable by other local users if the parent home-directory permissions allow traversal. The data directory may similarly be created with mode0755.This exceeds the minimum local access required by the declared functionality: only the account running the Skill needs to read or modify its invoice ledger. Although exploitation depends on host account and directory permissions, the code does not enforce this least-privilege boundary itself.
Attack Path
- A victim runs the
addcommand, causing the Skill to create~/.openclaw/smb-invoice-tracker/invoices.json. - The process uses a permissive umask, such as
022, and the victim's home-directory hierarchy permits traversal by another local account. - The resulting ledger is readable by users other than its owner.
- A local unprivileged attacker locates the predictable path and reads the file.
- The attacker obtains the stored payer identities, financial amounts, due dates, notes, payment status, ...[truncated 902 chars]
- A victim runs the
- Remediation
View remediation
Remediation Suggestions
-
Create the data directory with owner-only permissions:
python DATA_PATH.parent.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(DATA_PATH.parent, 0o700) -
Create the ledger using an explicit
0600mode rather than relying on the process umask. Useos.openwithO_CREAT,O_WRONLY, andO_TRUNC, then wrap the descriptor withos.fdopen. -
Write updates atomically to an owner-only temporary file in the same directory, flush and synchronize it, and replace the destination with
os.replace. This also reduces corruption risk if execution is interrupted. -
After replacement, explicitly enforce mode
0600on the resulting ledger. -
On startup or before reading existing data, verify that both the directory and ledger are owned by the current user and are not accessible to group or other users. Reject unsafe ownership and repair permissions where appropriate.
-
Apply the same owner-only permission controls to
config.json, which is written through a similar unrestricted pattern at lines 95–99.
-
