T09 · Insecure Skill Coding Practices
- Location
scripts/guard.js:200- Finding
Plaintext Persistent Storage of Sensitive User Profile and Intervention Data
- Content
View full analysis
Vulnerability Details
File Location:
scripts/guard.js:20-23, 148-204, 208-220
Vulnerability Type: Sensitive data stored persistently without encryption, restrictive permissions, or explicit consent
Risk Level: MediumVulnerable Code
javascript const WORKSPACE = process.env.HOME ? join(process.env.HOME, '.openclaw/workspace') : '/tmp/.openclaw/workspace'; const SOUL_DIR = join(WORKSPACE, '.soul'); const GUARD_STATE_PATH = join(SOUL_DIR, 'guard-state.json'); const VALUES_PATH = join(SOUL_DIR, 'user-values.json'); const INTERVENTIONS_PATH = join(SOUL_DIR, 'interventions.jsonl');javascript function saveUserValues(values) { ensureDir(SOUL_DIR); values.updated_at = new Date().toISOString(); writeFileSync(VALUES_PATH, JSON.stringify(values, null, 2), 'utf-8'); }javascript function recordIntervention(intervention) { ensureDir(SOUL_DIR); const interventionRecord = { ...intervention, id: `int_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`, timestamp: new Date().toISOString() }; try { const line = JSON.stringify(interventionRecord); appendFileSync(INTERVENTIONS_PATH, line + '\n', 'utf-8'); return interventionRecord.id; } catch (error) { console.error('记录干预失败:', error.message); return null; } }When
user-values.jsondoes not exist,loadUserValues()also automatically reads core values and work principles from.soul/user-profile.jsonand passes them tosaveUserValues(). This creates another persistent copy without an explicit user-consent check.Technical Analysis
The skill handles potentially sensitive behavioral and profile information, including core values, work principles, personal boundaries, value-drift assessments, and intervention messages. It persists this information as ordinary JSON and JSONL files at predictable paths under the user's workspace.
The writes do ...[truncated 2631 chars]
- Remediation
View remediation
Remediation Suggestions
-
Require informed consent before persistence
- Ask the user before importing profile values into a new persistent file.
- Clearly disclose which fields will be stored, why they are needed, and how long they will be retained.
- Support a memory-only mode and make sensitive-data persistence opt-in.
-
Enforce restrictive filesystem permissions
- Create
.soulwith mode0700. - Create sensitive files with mode
0600. - Verify and repair permissions on existing files before reading or writing them.
javascript mkdirSync(SOUL_DIR, { recursive: true, mode: 0o700 }); writeFileSync(VALUES_PATH, serializedValues, { encoding: 'utf8', mode: 0o600 }); - Create
-
Encrypt sensitive records at rest
- Use authenticated encryption such as AES-256-GCM.
- Store encryption keys in the operating system's credential store rather than beside the encrypted files.
- Implement key rotation and fail closed if secure key storage is unavailable.
-
Minimize and redact persisted content
- Do not persist complete generated intervention messages by default.
- Store only the minimum structured metadata required for rate limiting and effectiveness analysis.
- Remove or tokenize sensitive topics and free-form text before writing records.
- Apply the documented privacy classification rules before any record is persisted.
-
Implement retention and deletion controls
- Establish short, configurable retention periods.
- Rotate and delete old intervention records.
- Provide a user-accessible command to inspect, export, and permanently delete stored data.
-
Harden file handling
- Reject symbolic links and verify that resolved paths remain inside the expected workspace.
- Use atomic writes for JSON state files.
- Avoid the shared
/tmpfallback unless a private directory with verified ownership and mo ...[truncated 325 chars]
-
