T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/sync.py:190
- Finding
- Personal Contact Data Stored in a Plaintext Ontology File with Inherited Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sync.py:119-127` and `scripts/sync.py:190-205` **Vulnerability Type**: Plaintext sensitive-data storage and insufficient file-permission hardening **Risk Level**: Medium ### Complete Code Snippet ```python # Extract email email_match = re.search(r'\*\*Email:\*\*\s+([^\s\n]+)', content) if email_match: properties['email'] = email_match.group(1) # Extract phone phone_match = re.search(r'\*\*Phone:\*\*\s+([^\s\n]+)', content) if phone_match: properties['phone'] = phone_match.group(1) ``` ```python def write_ontology(self): """Write entities and relations to ontology file""" with open(self.graph_file, 'a') as f: # Write entities for entity in self.entities.values(): f.write(json.dumps({ 'op': 'upsert', 'entity': entity }) + '\n') # Write relations for relation in self.relations: f.write(json.dumps({ 'op': 'relate', **relation }) + '\n') print(f"📝 Wrote to {self.graph_file}") ``` ### Technical Analysis The extraction process copies email addresses and telephone numbers from Obsidian notes into entity properties. `write_ontology()` then serializes those properties directly into the append-only `graph.jsonl` file as plaintext JSON. The implementation creates the ontology directory with `Path.mkdir()` and opens the graph with the standard `open(..., 'a')` call, but it does not establish restrictive permissions such as `0700` for the directory and `0600` for the file. Consequently, access permissions are determined by the process umask, existing filesystem permissions, and any permissions already applied to an existing target. This also consolidates personal information from multiple notes into one predictable file. Such consolidation can increase the effect of unintended local disclosure. No network exfiltration or credential collection ...[truncated 1532 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Create the ontology directory with owner-only permissions and verify existing directory permissions: ```python self.ontology_path.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(self.ontology_path, 0o700) ``` 2. Create and append to the graph using an explicitly restricted descriptor: ```python fd = os.open( self.graph_file, os.O_WRONLY | os.O_CREAT | os.O_APPEND | os.O_NOFOLLOW, 0o600, ) with os.fdopen(fd, 'a', encoding='utf-8') as f: ... ``` 3. Check that the graph path is a regular file owned by the expected user before writing. Reject symbolic links and unexpected file types to prevent writes through attacker-controlled filesystem objects. 4. Add configuration controls allowing users to exclude sensitive properties such as email addresses and telephone numbers from the generated ontology. 5. Where the threat model includes other privileged services, backups, or shared storage, encrypt the ontology at rest and keep encryption keys outside the ontology directory. 6. Document clearly that extraction duplicates personal data into `graph.jsonl`, including the resulting retention and access-control implications. 7. Add automated tests that verify the generated directory is `0700`, the graph file is `0600`, and writes fail safely when the target is a symbolic link. ]]>
