Skill: aronchick/expanso-log-sanitize (version 1.0.0)

# log-sanitize "Sanitize log entries by removing passwords, tokens, and sensitive patterns" ## Requirements - Expanso Edge installed (`expanso-edge` binary in PATH) - Install via: `clawhub install expanso-edge` ## Usage ### CLI Pipeline ```bash # Run standalone echo '<input>' | expanso-edge run pipeline-cli.yaml ``` ### MCP Pipeline ```bash # Start as MCP server expanso-edge run pipeline-mcp.yaml ``` ### Deploy to Expanso Cloud ```bash expanso-cli job deploy https://skills.expanso.io/log-sanitize/pipeline-cli.yaml ``` ## Files | File | Purpose | |------|---------| | `skill.yaml` | Skill metadata (inputs, outputs, credentials) | | `pipeline-cli.yaml` | Standalone CLI pipeline | | `pipeline-mcp.yaml` | MCP server pipeline |

path: README.md
content: # log-sanitize

Sanitize log entries by removing sensitive data patterns like passwords, tokens, and API keys.

## Overview

This skill runs **entirely locally** without any API calls. It uses pattern matching to detect and redact common secret patterns in log files.

## Usage

### CLI Mode

```bash
# Sanitize a log line
echo 'user=admin [REDACTED_SECRET] [REDACTED_SECRET]' | \
  expanso-edge run pipeline-cli.yaml

# Sanitize an entire log file
cat /var/log/app.log | expanso-edge run pipeline-cli.yaml
```

### MCP Mode

```bash
# Start server
PORT=8080 expanso-edge run pipeline-mcp.yaml &

# Make request
curl -X POST http://localhost:8080/sanitize \
  -H "Content-Type: application/json" \
  -d '{"log": "[REDACTED_SECRET] [REDACTED_SECRET]"}'
```

## Output

```json
{
  "sanitized": "user=admin [REDACTED_SECRET] [REDACTED_SECRET]",
  "redactions": 27,
  "metadata": {
    "skill": "log-sanitize",
    "mode": "cli",
    "trace_id": "abc123...",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}
```

## Patterns Detected

| Pattern | Example | Replacement |
|---------|---------|-------------|
| Passwords | `[REDACTED_SECRET]` | `[REDACTED_SECRET]` |
| API Keys | `[REDACTED_SECRET]` | `[REDACTED_SECRET]` |
| Tokens | `[REDACTED_SECRET]` | `[REDACTED_SECRET]` |
| Bearer Auth | `Bearer eyJ...` | `Bearer ***REDACTED***` |
| AWS Keys | `AKIAIOSFODNN7` | `***AWS_KEY_REDACTED***` |
| JWT Tokens | `eyJ...eyJ...` | `***JWT_REDACTED***` |
| Secrets | `secret=xyz` | `[REDACTED_SECRET]` |

## Use Cases

- Pre-processing logs before sending to log aggregators
- Sanitizing logs before sharing with support
- Compliance with security policies
- Preparing logs for public documentation


path: pipeline-cli.yaml
content: # Expanso Pipeline: log-sanitize (CLI mode)
# ==========================================
#
# Usage:
#   echo '[REDACTED_SECRET]' | expanso-edge run pipeline-cli.yaml
#   cat app.log | expanso-edge run pipeline-cli.yaml
#
# No API key needed - runs entirely locally with pattern matching.

name: "log-sanitize-cli"
type: pipeline

config:
  input:
    stdin:
      codec: lines
      max_buffer: 10485760  # 10MB for large logs

  pipeline:
    processors:
      - mapping: |
          meta input_hash = content().hash("sha256").encode("hex")
          meta input_length = content().length()
          meta trace_id = uuid_v4()

          # Define patterns to redact
          let text = content()
          let redactions = 0
          let patterns_matched = []

          # Common secret patterns with regex-like matching
          # 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***")

          root.sanitized = $text
          root.redactions = content().length() - $text.length()
          root.metadata = {
            "skill": "log-sanitize",
            "mode": "cli",
            "input_hash": meta("input_hash"),
            "input_length": meta("input_length"),
            "trace_id": meta("trace_id"),
            "timestamp": now()
          }

      - log:
          level: INFO
          message: |
            [log-sanitize] Processed ${! meta("input_length") } chars (trace: ${! meta("trace_id").slice(0, 8) })

  output:
    stdout:
      codec: json_object


path: pipeline-mcp.yaml
content: # Expanso Pipeline: log-sanitize (MCP mode)
# ==========================================
#
# Usage:
#   PORT=8080 expanso-edge run pipeline-mcp.yaml &
#   curl -X POST http://localhost:8080/sanitize \
#     -d '{"log": "[REDACTED_SECRET] [REDACTED_SECRET]"}'
#
# No API key needed - runs entirely locally.

name: "log-sanitize-mcp"
type: pipeline

config:
  http:
    enabled: true
    address: "0.0.0.0:${PORT:-8080}"

  input:
    http_server:
      path: /sanitize
      allowed_verbs: [POST]
      timeout: 60s

  pipeline:
    processors:
      - mapping: |
          let input_log = this.log.or(content())
          meta input_hash = $input_log.hash("sha256").encode("hex")
          meta input_length = $input_log.length()
          meta trace_id = uuid_v4()

          let text = $input_log

          # 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***")

          root.sanitized = $text
          root.redactions = $input_log.length() - $text.length()
          root.metadata = {
            "skill": "log-sanitize",
            "mode": "mcp",
            "input_hash": meta("input_hash"),
            "input_length": meta("input_length"),
            "trace_id": meta("trace_id"),
            "timestamp": now()
          }

      - log:
          level: INFO
          message: |
            [log-sanitize] Processed ${! meta("input_length") } chars (trace: ${! meta("trace_id").slice(0, 8) })

  output:
    sync_response: {}


path: skill.yaml
content: # Skill: log-sanitize
# Version: 1.0.0
#
# Sanitize log entries by removing sensitive data patterns.
# Works without an LLM - uses local pattern matching.

name: "log-sanitize"
version: "1.0.0"
description: "Sanitize log entries by removing passwords, tokens, and sensitive patterns"

author:
  name: "Expanso Team"
  url: "https://expanso.io"
  avatar: "EX"

credentials: []  # No API key needed - runs entirely locally

inputs:
  - name: log
    type: string
    required: true
    description: Log entry or log file content to sanitize
  - name: patterns
    type: array
    default: ["password", "token", "secret", "key", "auth"]
    description: Additional patterns to redact

outputs:
  - name: sanitized
    type: string
    description: Log with sensitive data removed
  - name: redactions
    type: integer
    description: Number of redactions made
  - name: patterns_matched
    type: array
    description: List of patterns that matched

backends:
  - name: local
    type: local
    requires: []
    description: Pure local processing, no external calls

components:
  inputs:
    - stdin
    - http_server
  processors:
    - mapping
  outputs:
    - stdout
    - sync_response


path: test/fixtures/input.txt
content: 2024-01-15 10:30:45 INFO Starting application
2024-01-15 10:30:46 DEBUG Connecting to database with [REDACTED_SECRET]
2024-01-15 10:30:47 INFO API initialized with [REDACTED_SECRET]
2024-01-15 10:30:48 DEBUG Authorization header: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2024-01-15 10:30:49 INFO User authenticated successfully


path: test/test.yaml
content: # Test cases for log-sanitize skill

tests:
  - name: "Sanitize password"
    input: "user=admin [REDACTED_SECRET]"
    expected:
      has_field: sanitized
      metadata_has: [skill, trace_id, timestamp]

  - name: "Sanitize API key"
    input: "[REDACTED_SECRET]"
    expected:
      has_field: sanitized
      has_field: redactions

  - name: "Sanitize complex log"
    input_file: fixtures/input.txt
    expected:
      has_field: sanitized


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)