Back to skill

Security audit

Sensitive Data Masker

Security checks for vulnerabilities and agentic risk

Overview

This skill is intended to mask secrets, but its hook appears to fail open and may let original sensitive messages continue downstream while storing recoverable mappings locally.

Review this skill carefully before installing. Its goal is privacy-protective, but current artifacts indicate it may not actually mask messages before they reach downstream processing, and it stores recoverable secret mappings locally longer than users may expect unless manually cleaned.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
handler.js:32
Finding

Message-Masking Hook Fails Open Because stderr Is Configured as Ignored

Content
View full analysis
{ output += data.toString(); }); masker.stderr.on('data', (data) => { error += data.toString(); }); ``` The surrounding exception handler explicitly permits processing to continue with the original message: ```javascript } catch (error) { console.error('[sensitive-masker] Handler error:', error.message); // Error doesn't affect message processing, continue with original message } ``` ### Technical Analysis Node.js returns `null` for `masker.stderr` when the child process's stderr stream is configured as `"ignore"`. The subsequent call to `masker.stderr.on(...)` therefore throws a `TypeError`. The exception is caught by the outer handler, which neither blocks the message nor substitutes a safe fallback. Consequently, the handler exits before waiting for the Python process and before assigning the masked result to `event.context.content`. This is a deterministic fail-open condition in the security control. It undermines the Skill's primary declared purpose: preventing credentials and personally identifiable information from reaching the downstream LLM API. Passing the message as an element of an argument array does avoid shell interpolation, so this is not a command-injection finding. The issue is the inconsistent child-process stream configuration and unsafe failure policy. ### Attack Path 1. A user or attacker submits a message containing a password, API key, database URL, email address, or other detected sensitive value. 2. The `message:received` hook invokes the Python wrapper. 3. The c ...[truncated 1251 chars]
Remediation
View remediation
{ masker.once('error', reject); masker.once('close', resolve); }); ``` 3. Apply a fail-closed policy for this security boundary. If masking cannot be completed, do not forward the original message. Return an explicit processing error or replace the content with a safe placeholder. 4. Validate the wrapper output before modifying the event: ```javascript const result = JSON.parse(output); if (typeof result.masked !== 'string') { throw new Error('Invalid masker output'); } ``` 5. Add integration tests that: - Submit a message containing a known test credential. - Assert that `event.context.content` contains a mask marker rather than the credential. - Simulate Python startup failure, malformed JSON, and nonzero exit status. - Assert that every failure mode blocks or safely redacts the original content. 6. Apply the same correction to `handler.en.js`, or remove duplicated executable variants to prevent future security fixes from diverging. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
sensitive-masker.py:346
Finding

Expired Sensitive Records Are Not Automatically Deleted Despite Declared Auto-Cleanup

Content
View full analysis
int: """Clean expired data.""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute(''' DELETE FROM mappings WHERE expires_at < ? ''', (datetime.now().isoformat(),)) deleted = cursor.rowcount conn.commit() conn.close() # Clean cache now = datetime.now() expired_keys = [ k for k, v in self.cache.items() if v['expires_at'] < now ] for key in expired_keys: del self.cache[key] return deleted ``` The entry point invokes cleanup only when a user manually selects the `cleanup` command: ```python elif cmd == 'stats': show_stats() elif cmd == 'cleanup': cleanup() elif cmd == 'clear': clear_all() ``` No initialization path, periodic scheduler, hook execution path, or interval check invokes `cleanup_expired()` automatically. ### Technical Analysis The database query in `get()` prevents expired values from being restored through the normal API, but it does not remove those values from SQLite. The `expires_at` column therefore acts only as a logical access check until the separate manual cleanup command is run. Although stored values are protected with Fernet encryption, the encryption key and database are both retained under the same local data directory: - Database: `~/.opencl ...[truncated 2309 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (43)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The guide instructs users to back up the sensitive mapping file, but that file contains recoverable originals for masked secrets such as passwords, API keys, and connection strings. Backups duplicate the full secret store and often end up in less protected locations, so compromise of the backup would directly expose the underlying sensitive data and defeat the masking design.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The design explicitly restores masked secrets before task execution, re-exposing passwords, tokens, or connection strings to downstream components. Without an explicit user warning, consent model, or strict scoping of where restoration occurs, users may incorrectly assume masking provides end-to-end protection, leading to unintended secret disclosure during tool execution or logging.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The design persists original sensitive values in SQLite and in-memory cache for up to 7 days, creating a concentrated store of recoverable secrets. Even if encryption is mentioned elsewhere, this section does not guarantee encrypted-at-rest storage or user awareness of retention, so compromise of the host, backups, logs, or process memory could expose credentials and personal data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The design masks secrets before sending content to the LLM, but then restores them before downstream task execution without any explicit consent, warning, or policy boundary. That creates a re-exposure path where sensitive data may be passed into tools, scripts, connectors, or logs, defeating the privacy benefit of masking and potentially leaking credentials during execution.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The sample detector implementation hard-codes language='zh', and the configuration example also sets the Presidio language to zh. This appears to impose a specific locale by default without stating that users can choose or override the language, which conflicts with the language/locale policy criteria.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The design explicitly states that original sensitive data will be stored encrypted, but the earlier mapping-store code path writes and caches plaintext originals. In a skill whose core purpose is handling passwords, API keys, and other PII, storing recoverable plaintext in SQLite and memory significantly increases exposure if the local host, backups, logs, or filesystem are accessed.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · DESIGN.en.md (reported line 273)May include surrounding context.

bash
# Database file
chmod 600 ~/.openclaw/data/sensitive-masker/mapping.db

# Configuration file
chmod 600 ~/.openclaw/config/sensitive-masker.json

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · DESIGN.en.md (reported line 276)May include surrounding context.

bash
# Database file
chmod 600 ~/.openclaw/data/sensitive-masker/mapping.db

# Configuration file
chmod 600 ~/.openclaw/config/sensitive-masker.json

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · DESIGN.md (reported line 273)May include surrounding context.

bash
# Database file
chmod 600 ~/.openclaw/data/sensitive-masker/mapping.db

# Configuration file
chmod 600 ~/.openclaw/config/sensitive-masker.json

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · DESIGN.md (reported line 276)May include surrounding context.

bash
# Database file
chmod 600 ~/.openclaw/data/sensitive-masker/mapping.db

# Configuration file
chmod 600 ~/.openclaw/config/sensitive-masker.json

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The configuration snippet sets "language": "zh" as the illustrated operating language while the document elsewhere emphasizes broad language support. Without an explicit opt-in or user-choice mechanism, this is a natural-language locale policy concern.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · DESIGN.en.md (reported line 345)May include surrounding context.

return store.get(mask_id)

text

### 2️⃣ Batch Write

```python
def batch_add(self, items: list):

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · DESIGN.md (reported line 345)May include surrounding context.

return store.get(mask_id)

text

### 2️⃣ Batch Write

```python
def batch_add(self, items: list):

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README explicitly states that masked user content is sent to an API and that restoration mappings are stored for 7 days, but it does not prominently warn users that sensitive originals remain recoverable locally during that retention window. In a privacy-focused masking skill, this omission can mislead users into assuming data is fully protected or ephemeral, increasing the risk of unintended disclosure from local compromise, backups, or multi-user environments.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README instructs users to install zh_core_web_sm, which implies a Chinese-language processing requirement, but the document does not explain that the skill is limited to Chinese or provide an opt-in or alternative locale choice. This can violate language/locale policy because it imposes a specific locale assumption without justification in the user-facing text.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README explicitly describes sending masked user content to an API while also storing reversible mappings locally for 7 days, but it does not warn users about the privacy, retention, or re-identification implications of that design. Because the masking is reversible and the mapping store becomes a concentration point for secrets, users may unknowingly expose highly sensitive data despite believing it is safely redacted.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The example implementation hard-codes language='zh' in a masking function, which can cause the detector to analyze non-Chinese input with the wrong locale model. In a sensitive-data masking skill, this is security-relevant because missed detections can leave PII, credentials, or secrets unredacted and expose them downstream.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This example also fixes Presidio analysis to language='zh', creating the same risk of under-detecting entities when messages are in English or mixed-language content. Given this skill is intended for general sensitive-data masking, the context makes the issue more dangerous because users may assume broad protection while the implementation silently narrows coverage.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly states that original sensitive data is stored locally for 7 days and restored before task execution, but the documentation frames the flow as 'Safe' without a prominent warning about this retention and rehydration behavior. This can mislead users into believing secrets are only masked, when in reality plaintext secrets are persisted and later reintroduced into execution context, increasing exposure if the host, logs, or local storage are compromised.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The documented configuration sets Presidio language to zh, which indicates a locale-specific default. The file does not clearly offer user opt-in or explain why the skill is constrained to Chinese, creating a potential language/locale policy issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The markdown lists python3 sensitive-masker.py clear under management commands, but there is no warning that it clears all mappings and can prevent later restoration of masked data. For a destructive operation affecting stored user data, the skill description should explicitly disclose the consequence.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises shell-capable installation/command execution but does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization transparency gap: operators cannot easily tell that the skill may invoke shell commands, increasing the chance of unintended command execution during install or runtime.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The feature description emphasizes masking but does not clearly warn that the original sensitive values are retained locally for 7 days. Users may assume data is irreversibly redacted, when in fact recoverable plaintext secrets remain on disk, creating confidentiality and expectation risks if the host is compromised or shared.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The documentation presents masking as making LLM use 'safe' without an equally clear disclosure that values are restored before task execution. This can mislead users into believing downstream actions will operate only on masked data, while the system actually reintroduces secrets, increasing the chance of accidental disclosure or unsafe automated use.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The markdown includes a command to clear all mappings, which would remove restoration data and could permanently prevent recovery of masked values. The documentation does not warn that this action is destructive or irreversible.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
handler.en.js:32

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
handler.js:32

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
CHANNEL-MASKER-GUIDE.md:38