Skip to content

feat: WebMCP Security Scanner (CHK-WEB-001..008)#21

Open
0xChitlin wants to merge 2 commits intoMikeeBuilds:mainfrom
0xChitlin:feat/webmcp-security-checks
Open

feat: WebMCP Security Scanner (CHK-WEB-001..008)#21
0xChitlin wants to merge 2 commits intoMikeeBuilds:mainfrom
0xChitlin:feat/webmcp-security-checks

Conversation

@0xChitlin
Copy link

🛡️ WebMCP Security Scanner

Adds a new scanner category targeting Chrome 146's WebMCP API (navigator.modelContext) — the first security tooling for this emerging attack surface.

What is WebMCP?

Chrome 146 introduces WebMCP, allowing websites to declare structured services that AI agents can interact with directly (instead of navigating the human UI). While powerful, this opens new security vectors that ClawPinch should audit.

New Checks (8)

ID Severity Description
CHK-WEB-001 🔴 Critical Untrusted WebMCP origins
CHK-WEB-002 🟡 Warn Excessive capability declarations
CHK-WEB-003 🟡 Warn Unscoped modelContext grants
CHK-WEB-004 🔴 Critical Cross-origin service injection
CHK-WEB-005 🔴 Critical Data exfiltration via service access
CHK-WEB-006 🔴 Critical Prompt injection in service descriptions
CHK-WEB-007 🟡 Warn Missing service authentication
CHK-WEB-008 🟡 Warn Form auto-submission data leakage

Files Changed

  • scripts/scan_webmcp.sh — New scanner (840+ lines) with full JSON output, deep scan support, and helpers fallback
  • references/webmcp-threat-model.md — Comprehensive threat model covering 8 attack vectors
  • references/webmcp-checks.md — Detailed check catalog with evidence, remediation, and auto-fix
  • references/check-catalog.md — Updated with WebMCP section
  • SKILL.md — Updated to 71 checks across 9 categories
  • package.json — Version bump to 1.3.0, added webmcp keywords

Why This Matters

WebMCP is brand new (announced today) and nobody has built security tooling for it yet. This positions ClawPinch as the first-mover in WebMCP security auditing.

Context

Inspired by this tweet from @liadyosef about WebMCP + MCP Apps convergence toward agentic UI.


Total checks: 63 → 71 | Categories: 8 → 9 | Version: 1.2.1 → 1.3.0

Add new scan_webmcp.sh scanner targeting Chrome 146's WebMCP API
(navigator.modelContext) security risks. Introduces 8 new checks
across a new WebMCP category, bringing the total to 71 checks
across 9 categories.

New checks:
- CHK-WEB-001: Untrusted WebMCP origins (Critical)
- CHK-WEB-002: Excessive capability declarations (Warn)
- CHK-WEB-003: Unscoped modelContext grants (Warn)
- CHK-WEB-004: Cross-origin service injection (Critical)
- CHK-WEB-005: Data exfiltration via service access (Critical)
- CHK-WEB-006: Prompt injection in service descriptions (Critical)
- CHK-WEB-007: Missing service authentication (Warn)
- CHK-WEB-008: Form auto-submission data leakage (Warn)

Also includes:
- WebMCP threat model (references/webmcp-threat-model.md)
- Detailed check catalog (references/webmcp-checks.md)
- Updated main check-catalog.md with WebMCP section
- Version bump to 1.3.0
@gemini-code-assist
Copy link

Summary of Changes

Hello @0xChitlin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a critical new security scanner for the recently announced Chrome 146 WebMCP API. By adding a dedicated WebMCP security category with eight new checks and comprehensive documentation, the ClawPinch toolkit is positioned as a first-mover in auditing this emerging attack surface, significantly enhancing its capability to identify and mitigate novel AI agent-related vulnerabilities.

Highlights

  • New WebMCP Security Scanner: Introduced a new scanner category specifically targeting Chrome 146's WebMCP API, positioning ClawPinch as a first-mover in this emerging security domain.
  • Eight New Security Checks: Added 8 new security checks (CHK-WEB-001 to CHK-WEB-008) to audit various WebMCP attack vectors, including untrusted origins, excessive capabilities, and prompt injection.
  • Comprehensive Documentation: Included detailed documentation for the new WebMCP scanner, comprising a dedicated threat model and an expanded check catalog with evidence and remediation steps.
  • Toolkit Expansion and Version Update: Increased the total number of checks from 63 to 71 and categories from 8 to 9, alongside a version bump of the clawpinch toolkit to 1.3.0.
Changelog
  • SKILL.md
    • Updated the description to reflect 71 checks across 9 categories.
    • Added the 'WebMCP' category with 8 checks (CHK-WEB-001..008).
  • package.json
    • Updated the version from "1.2.1" to "1.3.0".
    • Added new keywords: "webmcp", "chrome-146", "model-context-protocol".
  • references/check-catalog.md
    • Appended a new "WebMCP (CHK-WEB)" section detailing 8 new checks (CHK-WEB-001 to CHK-WEB-008) with severity, description, and remediation for each.
  • references/webmcp-checks.md
    • Added a new file providing a complete reference for all 8 WebMCP security checks, including evidence, remediation, auto-fix details, and references to the threat model.
  • references/webmcp-threat-model.md
    • Added a new file outlining the WebMCP threat model, covering 8 distinct attack vectors with their threats, attack vectors, impacts, and mitigations.
  • scripts/scan_webmcp.sh
    • Added a new shell script implementing the WebMCP security scanner, including checks for untrusted origins, excessive capabilities, unscoped modelContext, cross-origin injection, data exfiltration, prompt injection, missing authentication, and form auto-submission risks.
Activity
  • No human activity (comments, reviews, etc.) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive WebMCP security scanner, significantly enhancing the security auditing capabilities of ClawPinch. While the new scan_webmcp.sh script is well-structured and robust, and the accompanying documentation is clear, a medium-severity argument injection vulnerability was identified. The CONFIG_PATH variable in scan_webmcp.sh is not properly sanitized before being passed to jq, which could allow an attacker to alter scanner behavior or exfiltrate data. Addressing this vulnerability is crucial to fully realize the security benefits of this scanner.

(.webmcp.endpoints // [])[] .url // empty,
(.mcpServers // {}) | to_entries[]? | .value.url // empty,
(.webmcp.trustedOrigins // [])[] // empty
' "$CONFIG_PATH" 2>/dev/null || true)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The script is vulnerable to argument injection. The $CONFIG_PATH variable, which can be controlled by the OPENCLAW_CONFIG_PATH environment variable (set on line 65), is passed directly to the jq command here. If an attacker sets this environment variable to a filename starting with a hyphen (e.g., -L /tmp/), jq will interpret the filename as a command-line option. This could allow an attacker to alter the scanner's execution, load untrusted jq modules, and potentially exfiltrate data from the configuration files being parsed.

Remediation:

To fix this, you should sanitize the CONFIG_PATH variable immediately after it is defined on line 65 to ensure it's always treated as a path. Add the following code snippet after line 65:

if [[ "$CONFIG_PATH" == -* ]]; then
  CONFIG_PATH="./$CONFIG_PATH"
fi

@greptile-apps
Copy link

greptile-apps bot commented Feb 10, 2026

Greptile Overview

Greptile Summary

This PR introduces a new WebMCP scanner (scripts/scan_webmcp.sh) and supporting documentation (references/webmcp-*.md), and updates published metadata (package.json, SKILL.md, and the global check catalog) to reflect 8 new checks under the CHK-WEB prefix.

In the codebase, scanners are invoked by the main orchestrator (clawpinch.sh) and emit JSON findings via emit_finding(); the new script follows that pattern and performs both config parsing (via jq) and workspace/skills file scanning.

Merge blockers found:

  • references/check-catalog.md still claims the catalog covers 63 checks, but WebMCP adds 8 more (71 total).
  • scripts/scan_webmcp.sh has correctness issues in its trust-matching logic (can mark untrusted URLs as trusted via substring matching) and in CHK-WEB-004’s origin grouping logic (jq group_by used without sorting), which can yield incorrect findings for critical checks.

Confidence Score: 3/5

  • This PR adds useful coverage but has a few correctness bugs that can produce incorrect WebMCP findings.
  • Core additions are straightforward docs/metadata plus a new scanner, but the scanner contains (1) overbroad trusted-origin matching and (2) incorrect jq origin grouping for CHK-WEB-004, and the check catalog header count is stale. These are fixable but should be addressed before merging to avoid misleading security results.
  • scripts/scan_webmcp.sh, references/check-catalog.md

Important Files Changed

Filename Overview
SKILL.md Updates advertised check/category counts and adds WebMCP category row, but leaves frontmatter version unchanged (still 1.2.0).
package.json Bumps package version to 1.3.0 and adds WebMCP-related keywords; no functional code changes.
references/check-catalog.md Adds WebMCP (CHK-WEB-001..008) entries to the global check catalog; content aligns with new scanner.
references/webmcp-checks.md Adds new WebMCP check reference doc describing evidence/remediation for CHK-WEB-001..008.
references/webmcp-threat-model.md Adds new WebMCP threat model document; no code changes.
scripts/scan_webmcp.sh Adds new WebMCP scanner script with 8 checks and deep-scan mode; contains correctness issues in origin parsing/trust matching and in cross-origin service grouping.

Sequence Diagram

sequenceDiagram
  participant User
  participant Orchestrator as clawpinch.sh
  participant WebMCP as scan_webmcp.sh
  participant FS as Filesystem
  participant JQ as jq

  User->>Orchestrator: Run clawpinch (--deep optional)
  Orchestrator->>WebMCP: Execute scan_webmcp.sh
  WebMCP->>JQ: Verify jq exists
  alt jq missing
    WebMCP-->>Orchestrator: Emit CHK-WEB-000 (critical)
  else jq present
    WebMCP->>FS: Read openclaw.json (CONFIG_PATH)
    WebMCP->>FS: Gather WebMCP-related files (workspace/skills)
    loop For each check (001..008)
      WebMCP->>JQ: Parse config fields for endpoints/services/tools
      WebMCP->>FS: Scan related files for patterns (origins/caps/descriptions)
      WebMCP-->>Orchestrator: Emit finding objects (0..n)
    end
    WebMCP-->>Orchestrator: Output findings as JSON array
  end
  Orchestrator-->>User: Render report / return JSON
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +74 to +90
TRUSTED_ORIGINS="${WEBMCP_TRUSTED_ORIGINS:-$DEFAULT_TRUSTED_ORIGINS}"
IFS=',' read -ra TRUSTED_ORIGIN_LIST <<< "$TRUSTED_ORIGINS"

# ---------------------------------------------------------------------------
# Sensitive capability keywords
# ---------------------------------------------------------------------------
SENSITIVE_CAPS=("filesystem" "shell" "exec" "network" "outbound" "process" "admin" "sudo" "root" "system" "os" "child_process" "spawn" "eval")
SENSITIVE_DATA_PATTERNS=("memory" "context" "history" "conversation" "agent_state" "session" "credentials" "secrets" "keychain" "token" "MEMORY.md" "SOUL.md" "USER.md")
PROMPT_INJECTION_PATTERNS=(
"ignore previous"
"ignore all previous"
"disregard"
"forget your instructions"
"new instructions"
"override"
"you are now"
"act as"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Host-only trust check

check_untrusted_origins extracts host (stripping scheme) and then calls is_trusted_origin "$host", but the default allow-list includes scheme prefixes like chrome-extension:// / moz-extension:// / safari-web-extension://. Those entries can never match a host-only string, so extension origins will always be flagged as untrusted. Either compare against the full origin (scheme://host[:port]) or remove scheme-based entries from DEFAULT_TRUSTED_ORIGINS and keep the list host-only.

Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/scan_webmcp.sh
Line: 74:90

Comment:
**Host-only trust check**

`check_untrusted_origins` extracts `host` (stripping scheme) and then calls `is_trusted_origin "$host"`, but the default allow-list includes scheme prefixes like `chrome-extension://` / `moz-extension://` / `safari-web-extension://`. Those entries can never match a host-only string, so extension origins will always be flagged as untrusted. Either compare against the full origin (`scheme://host[:port]`) or remove scheme-based entries from `DEFAULT_TRUSTED_ORIGINS` and keep the list host-only.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 1 to 6
---
name: clawpinch
description: "Security audit toolkit for OpenClaw deployments. Scans 63 checks across 8 categories. Use when asked to audit security, harden an installation, check for vulnerabilities, or review config safety."
description: "Security audit toolkit for OpenClaw deployments. Scans 71 checks across 9 categories. Use when asked to audit security, harden an installation, check for vulnerabilities, or review config safety."
version: "1.2.0"
author: MikeeBuilds
license: MIT
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version metadata mismatch

This PR updates the advertised check counts/categories and bumps package.json to 1.3.0, but SKILL.md frontmatter still says version: "1.2.0". If SKILL.md is treated as the canonical skill manifest, this will publish inconsistent version info.

Prompt To Fix With AI
This is a comment left during a code review.
Path: SKILL.md
Line: 1:6

Comment:
**Version metadata mismatch**

This PR updates the advertised check counts/categories and bumps `package.json` to `1.3.0`, but `SKILL.md` frontmatter still says `version: "1.2.0"`. If `SKILL.md` is treated as the canonical skill manifest, this will publish inconsistent version info.

How can I resolve this? If you propose a fix, please make it concise.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +123 to +133
is_trusted_origin() {
local origin="$1"
for trusted in "${TRUSTED_ORIGIN_LIST[@]}"; do
trusted="$(echo "$trusted" | xargs)" # trim whitespace
if [[ -z "$trusted" ]]; then continue; fi
# Exact match or substring match (origin starts with trusted prefix)
if [[ "$origin" == "$trusted" ]] || [[ "$origin" == "${trusted}"* ]] || [[ "$origin" == *"://${trusted}"* ]] || [[ "$origin" == *"://${trusted}:"* ]]; then
return 0
fi
done
return 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overbroad trust matching

is_trusted_origin() treats an origin as trusted if the trusted token appears anywhere after :// (e.g. [[ "$origin" == *"://${trusted}"* ]]). That can incorrectly mark an untrusted URL as trusted when the trusted domain appears in the path/query (e.g., a redirect param). Since CHK-WEB-001 is a critical trust boundary, this should only match the actual origin/host (or a well-defined prefix), not arbitrary substrings.

Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/scan_webmcp.sh
Line: 123:133

Comment:
**Overbroad trust matching**

`is_trusted_origin()` treats an origin as trusted if the trusted token appears anywhere after `://` (e.g. `[[ "$origin" == *"://${trusted}"* ]]`). That can incorrectly mark an untrusted URL as trusted when the trusted domain appears in the path/query (e.g., a redirect param). Since CHK-WEB-001 is a critical trust boundary, this should only match the actual origin/host (or a well-defined prefix), not arbitrary substrings.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +435 to +444
if [[ -f "$CONFIG_PATH" ]] && command -v jq &>/dev/null; then
# Extract all service declarations with their origins (null-safe)
local svc_origins
svc_origins=$(jq -r '
[(.webmcp.services // [])[] | {name: (.name // "unnamed"), origin: (.origin // null // "unknown")}] |
map(select(.origin != null)) |
group_by(.origin) |
map(select(length > 0) | {origin: .[0].origin, services: [.[].name]}) |
.[] | "\(.origin // "unknown")|\(.services | join(","))"
' "$CONFIG_PATH" 2>/dev/null || true)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect origin grouping

In CHK-WEB-004, the jq pipeline uses group_by(.origin) without sorting the array by .origin first (scripts/scan_webmcp.sh:438-444). In jq, group_by only groups adjacent equal keys, so repeated origins that aren’t contiguous can be split into multiple groups, leading to incorrect all_origins detection and false negatives/positives for origin-isolation warnings. Also origin: (.origin // null // "unknown") makes .origin never null, so map(select(.origin != null)) is ineffective.

Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/scan_webmcp.sh
Line: 435:444

Comment:
**Incorrect origin grouping**

In CHK-WEB-004, the jq pipeline uses `group_by(.origin)` without sorting the array by `.origin` first (`scripts/scan_webmcp.sh:438-444`). In jq, `group_by` only groups adjacent equal keys, so repeated origins that aren’t contiguous can be split into multiple groups, leading to incorrect `all_origins` detection and false negatives/positives for origin-isolation warnings. Also `origin: (.origin // null // "unknown")` makes `.origin` never null, so `map(select(.origin != null))` is ineffective.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link

greptile-apps bot commented Feb 10, 2026

Additional Comments (1)

references/check-catalog.md
Stale total check count

The header still says this is a reference for “all 63 checks” (references/check-catalog.md:3-4), but this PR adds the WebMCP section (8 new checks), bringing the total to 71. This makes the catalog internally inconsistent with SKILL.md/the PR metadata and will mislead users relying on the catalog as canonical.

Prompt To Fix With AI
This is a comment left during a code review.
Path: references/check-catalog.md
Line: 1:4

Comment:
**Stale total check count**

The header still says this is a reference for “all 63 checks” (`references/check-catalog.md:3-4`), but this PR adds the WebMCP section (8 new checks), bringing the total to 71. This makes the catalog internally inconsistent with `SKILL.md`/the PR metadata and will mislead users relying on the catalog as canonical.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant