T09 · Insecure Skill Coding Practices
- Location
scripts/setup.py:22- Finding
Sensitive Interaction Data Is Stored Without Restrictive Filesystem Permissions or Enforced Redaction
- Content
View full analysis
Vulnerability Details
File Location:
scripts/setup.py:22-31, 49-55, 67-73;scripts/log_observation.py:29-51;scripts/generate_report.py:324-333
Vulnerability Type: Plaintext sensitive-data storage with process-default permissions
Risk Level: MediumComplete Code Snippets
From
scripts/setup.py:python def setup_directories(base_path): """Create directory structure.""" directories = [ base_path, base_path / "sessions", base_path / "reports", ] for d in directories: d.mkdir(parents=True, exist_ok=True) print(f"✓ Created {d}")python config = { "study_active": True, "study_start_date": datetime.now().isoformat(), "survey_frequency": "after_each_task", "survey_style": "brief", "opted_out_topics": [], "participant_id": generate_participant_id() } with open(config_path, 'w') as f: json.dump(config, f, indent=2)python for f in [observations_file, surveys_file]: if not f.exists(): f.touch()From
scripts/log_observation.py:python def log_record(record): """Append a record (observation or survey) to the appropriate JSONL file.""" if not isinstance(record, dict): raise ValueError("Record must be a JSON object") record_type = record.get("_type", "observation") if record_type not in ("observation", "survey"): raise ValueError(f"Invalid _type: {record_type}. Must be 'observation' or 'survey'") # Route to correct file if record_type == "observation": filename = "observations.jsonl" else: filename = "surveys.jsonl" session_dir = get_today_dir() file_path = session_dir / filename # Append to JSONL with open(file_path, 'a') as f: f.write(json.dumps(record) + '\n') return str(file_path)From
scripts/generate_report.py:python # Generate report report = format_report(date_str, metrics, observations, surveys, gallery, end_o ...[truncated 2841 chars]- Remediation
View remediation
Remediation Suggestions
- Create the base directory and all session/report directories with owner-only permissions:
python base_path.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(base_path, 0o700)Apply equivalent restrictions to existing child directories.
-
Create configuration, JSONL, and report files atomically with mode
0600, rather than relying on the process umask. For example, useos.open()withO_CREATand an explicit mode, then wrap the descriptor withos.fdopen(). -
Correct permissions on pre-existing data during setup:
python os.chmod(directory, 0o700) os.chmod(file_path, 0o600)Avoid following untrusted symbolic links when modifying or opening these paths.
-
Implement centralized redaction before persistence. Detect and replace likely API keys, bearer tokens, passwords, private keys, financial identifiers, and other configured sensitive patterns in all nested fields.
-
Validate incoming records against strict observation and survey schemas. Reject unknown or oversized fields and normalize all free-text fields through the redaction layer.
-
Apply redaction again when generating reports so legacy or malformed records cannot copy sensitive values into another plaintext artifact.
-
Add configurable retention limits and secure deletion controls. Consider requiring explicit opt-in before passive verbatim collection and clearly expose the active collection state.
-
Add automated tests that run under permissive umasks and verify that every generated directory is
0700, every data file is0600, and representative secrets are redacted from both JSONL files and reports.
