T09 · Insecure Skill Coding Practices
- Location
scripts/presidio-scrub.py:6- Finding
Raw PII Can Be Transmitted to an Unrestricted Environment-Controlled Endpoint
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This PII-protection skill has a coherent local anonymization purpose, but it needs review because raw customer data can be sent to environment-controlled endpoints and reversible PII mapping files are handled unsafely.
Review before installing. This skill is not clearly malicious, but it handles sensitive customer data and currently relies on environment variables and caller-supplied session IDs without enough validation. Use only with trusted local Presidio endpoints, avoid untrusted session IDs, run as an unprivileged user, and consider fixing endpoint validation and mapping-file path handling first.
scripts/presidio-scrub.py:6Raw PII Can Be Transmitted to an Unrestricted Environment-Controlled Endpoint
scripts/presidio-scrub.py:27Path Traversal in Mapping Creation Enables File Overwrite and Permission Changes
scripts/presidio-restore.py:14Path Traversal in Restore Enables Unauthorized JSON Reads and Deletion
scripts/presidio-scrub.py:27Sensitive Mapping Files Are Created with Predictable Names and Race-Prone Permissions
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.
- Docker running locally (Colima recommended for headless Mac Mini, Docker Desktop also works)
- Python 3 (included with macOS)
- curl (included with macOS)
## Quick Install
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.
emoji: ""
requires:
bins:
- curl
- python3
- docker
files:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
- Docker running locally (Colima recommended for headless Mac Mini, Docker Desktop also works)
- Python 3 (included with macOS)
- curl (included with macOS)
## Quick Install
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.
### 1. Start Presidio containers
Create a docker-compose.yml at ~/.openclaw/presidio/ with analyzer (port 5002:3000) and anonymizer (port 5001:3000). Then run: docker compose up -d
IMPORTANT: Presidio containers listen on port 3000 internally. Map 5002:3000 (analyzer) and 5001:3000 (anonymizer). If you map 5002:5002, you'll get empty replies.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
- 100% local. Presidio containers run on localhost. No data leaves your machine.
- Fail-closed. If Presidio is down, the skill blocks data queries rather than sending unprotected PII.
- Ephemeral mappings. Token-to-real-value mapping files are created per request, stored with chmod 600, and auto-deleted after restore.
- Vanilla containers. Custom recognizers are passed via API calls, not baked into containers. Pull updated images anytime without losing your config.
## Why Colima over Docker Desktop?
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
- 100% local. Presidio containers run on localhost. No data leaves your machine.
- Fail-closed. If Presidio is down, the skill blocks data queries rather than sending unprotected PII.
- Ephemeral mappings. Token-to-real-value mapping files are created per request, stored with chmod 600, and auto-deleted after restore.
- Vanilla containers. Custom recognizers are passed via API calls, not baked into containers. Pull updated images anytime without losing your config.
## Why Colima over Docker Desktop?
The skill declares executable capabilities through required binaries and referenced scripts, but it does not define any explicit tool scope such as permissions or allowed-tools. In an agent environment, this creates unnecessary ambiguity about what the skill is allowed to access, increasing the risk of over-broad shell, network, environment, or file operations during use.
This JSON file repeatedly sets "supported_language": "en" for each recognizer, which enforces a single language/locale policy in natural-language configuration. The file provides no indication that users can choose another language or that the English-only restriction is required for a justified regional or compliance reason.
The manifest describes scrubbing customer data such as names, phones, emails, addresses, credit cards, and vessel names before sending data to AI models. This config additionally classifies a large curated list of South Florida, Houston, and statewide Florida city/place names as LOCATION, which materially broadens redaction beyond the stated examples and can remove ordinary geographic context that is not necessarily customer PII.
The manifest enumerates customer-data categories including names, phones, emails, addresses, credit cards, and vessel names, but this config also detects SEA_COOL_PROJECT identifiers. Project IDs are a distinct business identifier category not mentioned in the description, so the code's effective redaction scope is broader than the skill claims.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
analyzer_ok=false
anonymizer_ok=false
analyzer_status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "$ANALYZER_URL/analyze" \
-X POST -H "Content-Type: application/json" \
-d '{"text":"health check","language":"en"}' 2>/dev/null)
[ "$analyzer_status" = "200" ] && analyzer_ok=true
The manifest describes a skill focused on protecting PII by scrubbing customer data before model access, but this file performs the inverse operation by replacing tokens with original sensitive values. Reversible pseudonymization is mentioned in the manifest, yet the restoration capability materially expands behavior from protection/scrubbing into re-exposure of PII.
This code sends the input text to HTTP endpoints via http_post for analysis, and the script's only docstring describes anonymization and mapping storage, not that raw input is transmitted to separate services. For a code file handling sensitive data, this is a safety-relevant network operation lacking visible user disclosure such as a prompt, log message, or warning comment.
The script stores a reversible map from pseudonymous tokens back to original PII on disk, creating a concentrated secret that defeats anonymization if accessed by another local user, process, backup system, or compromised account. Although the file mode is set to 0600, the design still materially increases exposure because the mapping persists beyond the immediate operation and is written without any consent, retention, or encryption controls.
The file path used for the reversible PII mapping is derived from an environment-controlled base directory and an unsanitized session_id taken from argv. An attacker who can influence either value can cause path traversal or arbitrary file overwrite, which is especially sensitive here because the file contains reversible mappings from anonymized tokens back to original PII.
# Save mapping
os.makedirs(MAPPING_DIR, exist_ok=True)
mf = os.path.join(MAPPING_DIR, f"{session_id}.json")
with open(mf, "w") as f:
json.dump({"session_id": session_id, "created": int(time.time()), "reverse_map": reverse_map, "entity_count": len(entities_fwd), "entity_types": list(type_counters.keys())}, f, indent=2)
os.chmod(mf, 0o600)
The HTML comment includes the statement "I'm praying for you without your consent," which is natural-language content embedded in the skill file and directed at users or maintainers. This introduces an unsolicited personal/religious message that is not justified by the skill's function and can conflict with organizational expectations for neutral, consent-respecting communication.
This code performs outbound HTTP POST requests to analyzer and anonymizer services, which is a safety-relevant network operation for a code file. The script provides no confirmation prompt, visible notice, or explanatory comment/docstring about contacting external/local services, so users running it may not realize it transmits request data over HTTP.
The request body explicitly sets "language":"en", which imposes a specific language choice in natural-language behavior. There is no indication here that the skill offers language selection or that the English-only constraint is documented as intentional and justified.
No suspicious patterns detected.