Back to skill

Security audit

Webhook Router

Security checks for vulnerabilities and agentic risk

Overview

This webhook router is not clearly malicious, but it ships with a reusable public secret and unsafe routing/storage defaults that need careful review before use.

Do not deploy this as-is on a public endpoint. Rotate and remove the published hook token, require per-source secrets and provider signature verification, validate source names before any file or handler lookup, disable raw payload vault storage unless explicitly needed, and add redaction plus retention controls for logs and vault entries.

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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
register.sh:14
Finding

Published Hard-Coded Webhook Authentication Token

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
router.sh:176
Finding

Handler Path Traversal Can Execute Files Outside the Handler Directory

Content
View full analysis
&2 exit 1 fi ``` ```bash # router.sh:216 RESULT=$("$HANDLER" "$PAYLOAD" "$SOURCE" "$EVENT_TYPE" 2>&1) ``` ### Technical Analysis `SOURCE` can be supplied explicitly through `--source` or inherited from the webhook environment through `WEBHOOK_SOURCE`. It is not restricted to a safe identifier grammar before being concatenated into a filesystem path. A source containing `../` path components can escape `HANDLERS_DIR`. The `-x` check only determines whether the resulting path is executable; it does not verify that its canonical location remains under the approved handler directory. If the resolved path exists and is executable, the router invokes it. The `.sh` suffix limits candidate filenames, but it does not prevent traversal. For example, a value shaped like `../../../some/path/worker` resolves to an attempted target ending in `worker.sh` outside `handlers/`. ### Attack Path 1. An attacker gains the ability to invoke the router or submit a webhook that controls `WEBHOOK_SOURCE`. The published shared token may provide this ability in affected deployments. 2. The attacker identifies an executable `.sh` file reachable by ...[truncated 1274 chars]
Remediation
View remediation
&2 exit 1 fi ``` 2. Resolve handlers through a fixed mapping rather than constructing paths from request data: ```bash case "$SOURCE_TYPE" in github) HANDLER="$HANDLERS_DIR/github.sh" ;; custom) HANDLER="$HANDLERS_DIR/custom.sh" ;; *) HANDLER="$HANDLERS_DIR/generic.sh" ;; esac ``` 3. If dynamic handler names are required, canonicalize the candidate and verify containment: ```bash handlers_real=$(realpath "$HANDLERS_DIR") candidate_real=$(realpath -e "$candidate") || exit 1 [[ "$candidate_real" == "$handlers_real/"* ]] || exit 1 ``` 4. Reject `/`, `\`, dot segments, control characters, whitespace, and encoded path separators. 5. Do not treat request-provided environment variables as trusted routing configuration. 6. Run handlers under a dedicated, minimally privileged account with a restricted environment. 7. Add tests covering `../`, absolute paths, symbolic links, encoded separators, and overlong identifiers. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
register.sh:78
Finding

Unvalidated Registration Type Enables File Writes Outside Intended Directories

Content
View full analysis
- NORMALIZED_NAME=$(echo "$NAME" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9_-') SOURCE_ID="${SOURCE_TYPE}-${NORMALIZED_NAME}" # Generate unique token for this source (optional extra validation) SOURCE_TOKEN=$(openssl rand -hex 16 2>/dev/null || head -c 32 /dev/urandom | xxd -p | tr -d '\n') # Save registration REGISTRATION_FILE="${CONFIG_DIR}/${SOURCE_ID}.json" ... echo "$REGISTRATION" > "$REGISTRATION_FILE" ``` ```bash # register.sh:113-119 if [[ "$CREATE_HANDLER" == "true" ]]; then HANDLER_FILE="${HANDLERS_DIR}/${SOURCE_TYPE}.sh" if [[ -f "$HANDLER_FILE" ]]; then echo "Note: Handler for '$SOURCE_TYPE' already exists at $HANDLER_FILE" else cat > "$HANDLER_FILE" << 'HANDLER_TEMPLATE' ``` ```bash # register.sh:173-176 # Replace template variables sed -i.bak "s/{{SOURCE_TYPE}}/${SOURCE_TYPE}/g" "$HANDLER_FILE" && rm -f "${HANDLER_FILE}.bak" chmod +x "$HANDLER_FILE" ``` ### Technical Analysis Although `NAME` is normalized, `SOURCE_TYPE` is accepted without validation. It is incorporated into: - `SOURCE_ID`, which is used in the registration JSON path. - `HANDLER_FILE`, which is created when `--handler` is specified. - A dynamically constructed `sed` replacement expression. Path separators and `..` components in `SOURCE_TYPE` can therefore escape `.config` or `handlers`. The generated shell template has fixed primary content, so this is not a direct primitive for writing fully arbitrary bytes. Nevertheless, it permits unintended `.sh` file creation or ...[truncated 1677 chars]
Remediation
View remediation
"$REGISTRATION_FILE" ``` 5. Check for symbolic links before writing and use secure file-creation mechanisms where available. 6. Replace dynamic `sed` interpolation with a safe templating method that passes the value as data rather than executable substitution syntax. 7. Apply restrictive permissions to registration files because they contain source tokens: ```bash umask 077 ``` 8. Separate registration administration from the public webhook runtime and restrict who can invoke `register.sh`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
router.sh:99
Finding

Webhook Provider Signatures Are Detected but Never Verified

Content
View full analysis
/dev/null || echo "unknown") SOURCE="github-${REPO//\//-}" # Check for GitLab elif [[ -n "${HTTP_X_GITLAB_EVENT:-}" ]]; then PROJECT=$(echo "$PAYLOAD" | jq -r '.project.path_with_namespace // "unknown"' 2>/dev/null || echo "unknown") SOURCE="gitlab-${PROJECT//\//-}" # Check for Stripe elif [[ -n "${HTTP_STRIPE_SIGNATURE:-}" ]]; then SOURCE="stripe-webhook" ``` ```text # SKILL.md:184-192 ### GitHub 1. Go to repository → Settings → Webhooks → Add webhook 2. Payload URL: https://gregs-mac-mini.taila31444.ts.net/hooks?source=github- 3. Content type: application/json 4. Secret: (leave blank, token is in header) 5. Events: Select events or "Let me select individual events" 6. Add header: X-Hook-Token: 19e78f0288d476ee1197d4b374b6f73394abe121c12cc38a ``` ### Technical Analysis The router checks whether GitHub, GitLab, or Stripe-related headers are present, but it never validates their cryptographic values. A header's presence is used only as a source-classification hint. The documentation explicitly advises leaving the GitHub webhook secret blank. As a result, there is no provider-specific proof that a payload was created by GitHub, Stripe, or another claimed sender. All integrations instead rely on one shared OpenClaw hook token. A shared gateway token may protect endpoint access, but it doe ...[truncated 1396 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
handlers/custom.sh:41
Finding

Webhook Payloads Are Persisted Without Field-Level Redaction

Content
View full analysis
/dev/null; then vault write "$path" --data "$content" --tags "$tags" 2>/dev/null || true fi } ``` ```bash # handlers/custom.sh:41-54 VAULT_CONTENT=$(jq -n \ --arg ts "$TIMESTAMP" \ --arg source "$SOURCE" \ --arg event "$EVENT_TYPE" \ --arg payload "$PAYLOAD" \ '{ timestamp: $ts, source: $source, event_type: $event, payload: $payload }') write_to_vault "${VAULT_SECTION}/${SOURCE}/${TIMESTAMP}" "$VAULT_CONTENT" "custom,webhook" ``` ```bash # handlers/generic.sh:55-62 # Truncate payload if too large PAYLOAD_SIZE=$(echo "$PAYLOAD" | wc -c) if [[ $PAYLOAD_SIZE -gt $LOG_LIMIT ]]; then TRUNCATED_PAYLOAD=$(echo "$PAYLOAD" | head -c $LOG_LIMIT) TRUNCATED_PAYLOAD="${TRUNCATED_PAYLOAD}... [truncated, total size: $PAYLOAD_SIZE bytes]" else TRUNCATED_PAYLOAD="$PAYLOAD" fi ``` ```bash # handlers/generic.sh:107-133 VAULT_CONTENT=$(jq -n \ --arg ts "$TIMESTAMP" \ --arg source "$SOURCE" \ --arg event "$EVENT_TYPE" \ --arg identifier "$IDENTIFIER" \ --arg service "$SERVICE_NAME" \ --arg event_name "$EVENT_NAME" \ --arg status "$STATUS" \ --arg user "$USER" \ --arg payload "$TRUNCATED_PAYLOAD" \ --argjson important "$IS_IMPORTANT" \ '{ timestamp: $ts, source: $source, event_type: $event, identifier: $identifier, service: $service, event_name: $event_name, status: $status, user: $user, payload: $p ...[truncated 2043 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (15)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill publishes what appears to be a live hook authentication token and instructs users to reuse it in multiple webhook configurations. Exposing a bearer-style secret in documentation enables unauthorized parties to send forged webhooks to the public endpoint, potentially triggering actions, alerts, or downstream automation as trusted events.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 310)May include surrounding context.

md
- `SKILL.md` - This documentation

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The generated handler forwards the complete webhook payload into a vault entry, which may transmit and retain highly sensitive information such as customer data, API metadata, or embedded secrets. Because this happens in a template intended for broad reuse and there is no warning or filtering, it increases the chance of compliance, privacy, and secret-exposure issues across all deployed handlers.

Content

No source excerpt is available for this finding.

Chaining Abuse

High
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Content

Scanner excerpt · register.sh (reported line 196)May include surrounding context.

sh
HANDLER_TEMPLATE

        # Replace template variables
        sed -i.bak "s/{{SOURCE_TYPE}}/${SOURCE_TYPE}/g" "$HANDLER_FILE" && rm -f "${HANDLER_FILE}.bak"
        chmod +x "$HANDLER_FILE"
        
        echo "Created handler template: $HANDLER_FILE"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 248)May include surrounding context.

Sends mock webhooks to test the routing system.

Manual Test with curl

bash
# GitHub push event

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation says webhook payloads are logged as hashes, but the handler example explicitly writes the full payload to vault storage. That discrepancy can cause operators to handle sensitive webhook data under false assumptions, increasing the risk of credential, token, or personal data retention and disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script stores the full webhook payload in the vault verbatim, and webhook payloads often contain secrets, personal data, tokens, headers, URLs, or attacker-controlled content. Persisting all received data without minimization, filtering, or any disclosure increases the risk of sensitive-data retention, insider exposure, and later compromise if the vault contents are accessed or exported.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file header describes this as a generic webhook handler that 'logs payload and attempts to extract meaningful information.' In practice, the script writes structured webhook data including payload content to a vault (L107-L133) and sends alert messages for important events (L157-L167), which are materially different side effects from simple logging.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The handler stores attacker-controlled webhook payload content into persistent vault storage by default, including potentially sensitive fields such as user identifiers, emails, status details, and raw payload fragments. In a generic webhook handler, this is dangerous because unknown third-party sources can cause sensitive, excessive, or maliciously crafted data to be retained indefinitely, increasing exposure in the event of vault misuse, over-retention, or downstream access by other tools.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

When a payload is classified as important, the script sends extracted webhook details to an external alert channel, potentially exposing sensitive operational, user, or incident information to systems outside the primary processing boundary. This is particularly risky in a generic webhook context because untrusted payload content can influence alert content, causing unintended data disclosure or message injection into downstream notification systems.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script writes registration data including a per-source token to a local JSON file under .config without setting restrictive permissions or warning the user that secret material is being persisted. If the workspace is shared, backed up, or readable by other local users/processes, the token could be exposed and used to spoof webhook traffic if relied on elsewhere.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The generated webhook handler stores full inbound payloads in a vault path, which can capture secrets, PII, tokens, or proprietary event contents unrelated to the operational need of handling the webhook. In this context the behavior is built into an auto-generated template and not clearly disclosed, making accidental over-collection and sensitive data retention likely.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code logs source, event type, repository, sender, payload hash, and handler information to a persistent file via LOG_FILE. For code files, safety-relevant data handling should have some visible disclosure such as a prompt, print statement, or explanatory comment/docstring, but the script performs the write silently from the user's perspective.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This line executes another shell script as a subprocess and passes it the webhook payload and derived metadata. Although subprocess execution is core behavior for a router, there is no visible runtime disclosure that untrusted webhook content will be handed off to executable handlers, which fits the missing-warning criteria for code files.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The file header describes the script as a GitHub webhook handler that processes events and triggers appropriate actions. In practice, those actions include writing structured repository event data to a vault and sending notifications through an external message tool, which are meaningful side effects not reflected in the documentation.

Content

No source excerpt is available for this finding.