Back to skill

Security audit

Expanso log-sanitize

Security checks for vulnerabilities and agentic risk

Overview

This log-sanitizing skill is coherent and not malicious, but it should be reviewed because it handles secrets, exposes an unauthenticated HTTP endpoint on all interfaces, and its redaction rules can miss common secret formats.

Install only if you understand its limits: use the CLI mode or bind the MCP service to localhost, avoid exposing the HTTP endpoint to a network without authentication and request limits, and do not rely on this as a complete sanitizer for JSON, quoted, multiline, or unusual credential formats.

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
pipeline-cli.yaml:30
Finding
Incomplete Redaction Patterns Can Leak Sensitive Credentials<![CDATA[ ## Vulnerability Details **File Location**: `pipeline-cli.yaml:30-48`; mirrored in `pipeline-mcp.yaml:33-51` **Vulnerability Type**: Incomplete sensitive-data sanitization **Risk Level**: High ### Vulnerable Code ```yaml # Password patterns let text = $text.re_replace_all("(?i)(password|passwd|pwd)\\s*[=:]\\s*[^\\s,;\"']+", "$1=***REDACTED***") # Token/API key patterns let text = $text.re_replace_all("(?i)(token|api[_-]?key|apikey|access[_-]?key|secret[_-]?key)\\s*[=:]\\s*[^\\s,;\"']+", "$1=***REDACTED***") # Bearer tokens let text = $text.re_replace_all("(?i)bearer\\s+[a-zA-Z0-9._-]+", "Bearer ***REDACTED***") # AWS keys let text = $text.re_replace_all("AKIA[0-9A-Z]{16}", "***AWS_KEY_REDACTED***") # Generic secret patterns let text = $text.re_replace_all("(?i)(secret|auth|credential|private[_-]?key)\\s*[=:]\\s*[^\\s,;\"']+", "$1=***REDACTED***") # JWT tokens (three base64 sections separated by dots) let text = $text.re_replace_all("eyJ[a-zA-Z0-9_-]*\\.eyJ[a-zA-Z0-9_-]*\\.[a-zA-Z0-9_-]+", "***JWT_REDACTED***") ``` The MCP pipeline contains the same effective patterns: ```yaml # Password patterns let text = $text.re_replace_all("(?i)(password|passwd|pwd)\\s*[=:]\\s*[^\\s,;\"']+", "$1=***REDACTED***") # Token/API key patterns let text = $text.re_replace_all("(?i)(token|api[_-]?key|apikey|access[_-]?key|secret[_-]?key)\\s*[=:]\\s*[^\\s,;\"']+", "$1=***REDACTED***") # Bearer tokens let text = $text.re_replace_all("(?i)bearer\\s+[a-zA-Z0-9._-]+", "Bearer ***REDACTED***") # AWS keys let text = $text.re_replace_all("AKIA[0-9A-Z]{16}", "***AWS_KEY_REDACTED***") # Generic secret patterns let text = $text.re_replace_all("(?i)(secret|auth|credential|private[_-]?key)\\s*[=:]\\s*[^\\s,;\"']+", "$1=***REDACTED***") # JWT tokens let text = $text.re_replace_all("eyJ[a-zA-Z0-9_-]*\\.eyJ[a-zA-Z0-9_-]*\\.[a-zA-Z0-9_-]+", "***JWT_REDACTED***") ``` ### Technical Analysis The password, token, and generic-secret expressions assume that a sensitive key is fol ...[truncated 2531 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse structured formats such as JSON rather than applying regular expressions to serialized data. Recursively redact values whose normalized keys match the sensitive-key policy. 2. Add patterns that safely support single-quoted and double-quoted keys and values, escaped characters, and values containing whitespace or punctuation. 3. Preserve the complete secret boundary when redacting multiline or delimited values instead of stopping at the first space, comma, or semicolon. 4. Introduce a fail-closed or warning mechanism for malformed and uncertain input. The result should indicate when complete sanitization cannot be guaranteed. 5. Calculate `redactions` from the number of successful replacements rather than the output-length difference. 6. Implement the declared `patterns` input and `patterns_matched` output, or remove them from `skill.yaml` so callers do not rely on unsupported controls. 7. Add regression tests covering: - JSON and nested JSON fields - Single-quoted and double-quoted values - Secrets containing spaces, commas, semicolons, slashes, and Unicode - Escaped quotes - Multiline credentials - Authorization-header variants - Near-match and partial-match cases 8. Apply the same corrected implementation and tests to both CLI and MCP pipelines to prevent divergent security behavior. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
pipeline-mcp.yaml:15
Finding
Unauthenticated HTTP Sanitization Service Listens on All Network Interfaces<![CDATA[ ## Vulnerability Details **File Location**: `pipeline-mcp.yaml:15-24` **Vulnerability Type**: Unnecessary network exposure and missing access controls **Risk Level**: Medium ### Vulnerable Code ```yaml config: http: enabled: true address: "0.0.0.0:${PORT:-8080}" input: http_server: path: /sanitize allowed_verbs: [POST] timeout: 60s ``` ### Technical Analysis The MCP-mode HTTP service binds to `0.0.0.0`, making it listen on every available network interface rather than only the loopback interface. The configuration does not define authentication or authorization for the `/sanitize` endpoint. It also does not specify an application-level request-size limit or rate limit. This configuration exposes a service described as running locally to other systems whenever host firewall and network policy permit access. Restricting the endpoint to POST requests does not authenticate clients or prevent resource-exhaustion attempts. The 60-second timeout can allow each accepted request to retain processing resources for a significant period. Concurrent submissions or large payloads could impose CPU and memory pressure during hashing, regular-expression processing, and response construction. ### Attack Path 1. An operator starts the MCP pipeline using the documented command, which defaults to port 8080. 2. Because the service binds to `0.0.0.0`, it becomes reachable through any host interface allowed by the surrounding firewall or container configuration. 3. A network-adjacent or otherwise reachable attacker discovers the open port. 4. The attacker sends unauthenticated POST requests to `/sanitize`. 5. The attacker repeatedly submits requests, potentially with large log bodies or high concurrency. 6. The service spends resources hashing, scanning, rewriting, and returning attacker-controlled data, potentially degrading availability for legitimate users. ### Impact Assessment The endpoint does not directly provide operating- ...[truncated 533 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind to loopback by default: ```yaml address: "127.0.0.1:${PORT:-8080}" ``` 2. Require an explicit, security-reviewed configuration option before listening on external interfaces. 3. Add authentication for remote deployments, such as a securely managed bearer credential or mutually authenticated TLS. 4. Enforce authorization independently of the request method. 5. Configure strict request-body size, header size, concurrency, and rate limits at the application or reverse-proxy layer. 6. Use short, appropriate request and idle timeouts and cap the amount of work performed for one request. 7. Place remotely accessible deployments behind a hardened reverse proxy with TLS and network access controls. 8. Run the service with minimal operating-system permissions and resource limits. 9. Document that binding to `0.0.0.0` creates network exposure and provide secure deployment examples. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

External Transmission

Medium
Category
Data Exfiltration
Content
PORT=8080 expanso-edge run pipeline-mcp.yaml &

# Make request
curl -X POST http://localhost:8080/sanitize \
  -H "Content-Type: application/json" \
  -d '{"log": "password=secret123 api_key=sk-123abc"}'
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
#
# Usage:
#   PORT=8080 expanso-edge run pipeline-mcp.yaml &
#   curl -X POST http://localhost:8080/sanitize \
#     -d '{"log": "password=secret123 token=abc123"}'
#
# No API key needed - runs entirely locally.
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Vague Triggers

Low
Confidence
88% confidence
Finding
The pipeline exposes an HTTP server on 0.0.0.0 with no authentication, and the endpoint accepts arbitrary POST data for processing. Even though the function is local log sanitization, binding to all interfaces makes the service reachable from other hosts and can enable unauthorized use, data exposure to the service, or abuse for denial of service via large or frequent requests.

Static analysis

No suspicious patterns detected.