T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/add_cpa_question.py:16
- Finding
- Sensitive tax records are created without restrictive filesystem permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/add_cpa_question.py:16-30` **Additional Locations**: `scripts/add_document.py:15-29`; `scripts/archive_year.py:17-31`; `scripts/capture_event.py:21-35`; `scripts/generate_summary.py:21-23, 180, 267`; `scripts/log_notice.py:16-30`; `scripts/prep_meeting.py:19-21, 193`; `scripts/set_year_state.py:29-43`; `scripts/track_expense.py:16-30` **Vulnerability Type**: Insecure permissions for sensitive local data **Risk Level**: Medium ### Vulnerable Code ```python def ensure_base_dir() -> None: os.makedirs(BASE_DIR, exist_ok=True) def load_questions() -> Dict[str, Any]: if os.path.exists(QUESTIONS_FILE): with open(QUESTIONS_FILE, "r", encoding="utf-8") as f: return json.load(f) return {"questions": []} def save_questions(data: Dict[str, Any]) -> None: ensure_base_dir() with open(QUESTIONS_FILE, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) ``` The same creation pattern is used by the other listed scripts for tax documents, expenses, notices, annual state, and generated summaries. ### Technical Analysis The application stores sensitive tax and financial records in the predictable directory `~/.openclaw/workspace/memory/tax/`. It creates the directory with `os.makedirs()` and writes files with ordinary `open(..., "w")` calls, but it does not set or verify restrictive permission modes. Consequently, permissions are determined by the process umask. Under a common umask of `0022`, directories can be created with mode `0755` and files with mode `0644`. On a multi-user system, these modes may allow other local users to traverse the tax-memory directory and read files containing: - Tax document types, issuers, and amounts - Expense amounts and descriptions - Tax-authority notice summaries and deadlines - CPA questions and linked records - Raw natural-language input - Generated Markdown and CSV handoff reports The issue does ...[truncated 1359 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the base directory with owner-only permissions: ```python os.makedirs(BASE_DIR, mode=0o700, exist_ok=True) os.chmod(BASE_DIR, 0o700) ``` 2. Create output files with mode `0600`, rather than relying on the process umask. For example: ```python fd = os.open( QUESTIONS_FILE, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600, ) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) ``` 3. Use atomic writes: create a temporary file inside the protected directory with mode `0600`, flush and synchronize it, and then replace the destination with `os.replace()`. 4. Audit and correct existing permissions for the base directory, JSON records, generated Markdown files, and CSV reports. 5. Centralize secure storage operations in one shared helper so every script applies identical permission and atomic-write controls. 6. Consider optional encryption at rest where the threat model includes other privileged local software, shared workstations, or untrusted backup systems. 7. Add automated tests that run with a permissive umask and verify that directories remain `0700` and files remain `0600`. ]]>
