Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ClawPinch

Security audit toolkit for OpenClaw deployments. Scans 63 checks across 8 categories (configuration, secrets, network, skills, permissions, cron, CVE, supply chain).
Security audit toolkit for OpenClaw deployments. Scans 63 checks across 9 categories (configuration, secrets, network, skills, permissions, cron, CVE, supply chain, integrity).

## Quick Start

Expand Down Expand Up @@ -40,7 +40,9 @@ clawpinch/
│ ├── scan_permissions.sh # CHK-PRM-001..008 — least-privilege, wildcards
│ ├── scan_crons.sh # CHK-CRN-001..006 — sandbox, timeouts, privilege
│ ├── scan_cves.sh # CHK-CVE-001..005 — known vulns, outdated deps
│ └── scan_supply_chain.sh # CHK-SUP-001..008 — registry trust, hash verify
│ ├── scan_supply_chain.sh # CHK-SUP-001..008 — registry trust, hash verify
│ ├── scan_integrity.sh # CHK-INT-001 — reference data integrity verification
│ └── update_checksums.sh # Regenerate SHA256 checksums for reference data
├── references/
│ ├── known-cves.json # CVE database for version checks
│ ├── malicious-patterns.json # Known bad skill hashes
Expand Down Expand Up @@ -133,3 +135,19 @@ When adding a new auto-fix command to a scanner:
5. Document the security rationale

**IMPORTANT: Never use eval() directly in new code.** Always use `safe_exec_command()` for command execution. This prevents command injection attacks via compromised reference files or malicious findings.

## Reference Data Integrity

ClawPinch protects its own reference data files using SHA256 checksums:

- **What's protected:** `references/known-cves.json` and `references/malicious-patterns.json`
- **How it works:** Each `.json` file has a `.json.sha256` checksum file. On every scan, `scan_integrity.sh` verifies the hash matches.
- **When verification fails:** A critical finding (CHK-INT-001) is emitted. This could indicate file corruption or tampering.
- **Updating reference data:** After modifying any `.json` file in `references/`, run `bash scripts/update_checksums.sh` to regenerate checksums.

**Common.sh helper:**
```bash
verify_json_integrity <json_file_path> # Returns 0 if valid, 1 if failed
```

This prevents supply-chain attacks where an attacker modifies ClawPinch's CVE database or malicious pattern signatures to hide real threats.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ bash clawpinch.sh

- **63 checks** across 8 scanner categories
- **Parallel scanner execution** -- 1.5-3x faster scans by running all scanners concurrently (use `--sequential` for debugging)
- **Integrity verification** -- SHA256 checksums protect reference data from tampering
- **Structured JSON output** for programmatic consumption
- **Interactive review mode** with one-by-one fix workflow
- **Auto-fix commands** for findings that support automated remediation
Expand Down Expand Up @@ -414,6 +415,12 @@ The terminal UI features:
| 007 | Registry certificate not pinned | Warn |
| 008 | Skill author identity not verified | Warn |

### Integrity (CHK-INT)

| ID | Check | Severity |
|-----|------------------------------------------------|----------|
| 001 | Reference data integrity check failed | Critical |

---

## Security
Expand Down Expand Up @@ -450,6 +457,8 @@ clawpinch/
scan_crons.sh # Cron scanner
scan_cves.sh # CVE scanner
scan_supply_chain.sh # Supply chain scanner
scan_integrity.sh # Reference data integrity scanner
update_checksums.sh # Regenerate reference data checksums
references/
known-cves.json # CVE database
malicious-patterns.json # ClawHavoc signatures
Expand All @@ -468,6 +477,44 @@ clawpinch/

---

## Maintaining Reference Data

ClawPinch uses SHA256 checksums to verify the integrity of reference JSON files (`known-cves.json`, `malicious-patterns.json`). This protects against tampering that could cause the tool to miss real threats or inject malicious auto-fix commands.

### How Integrity Verification Works

- Each `.json` file has a corresponding `.json.sha256` checksum file
- On every scan, ClawPinch verifies the SHA256 hash matches the expected value
- If verification fails, a **critical** finding (CHK-INT-001) is emitted
- The scan continues, but you'll be warned that reference data may be compromised

### When Verification Fails

If you see `CHK-INT-001: Reference data integrity check failed`:

1. **Check if you modified the files** -- If you intentionally updated reference data, regenerate checksums (see below)
2. **Suspect tampering** -- If you didn't modify the files, restore them from a trusted source (GitHub release, npm package, or fresh clone)
3. **Review the evidence** -- The finding will list which files failed verification

### Updating Reference Data

When you update `known-cves.json` or `malicious-patterns.json`, you **must** regenerate the checksums:

```bash
# Regenerate all checksums in references/ directory
bash scripts/update_checksums.sh
```

This script:
- Finds all `.json` files in `references/`
- Computes SHA256 hashes (using `sha256sum` on Linux, `shasum -a 256` on macOS)
- Writes `.json.sha256` files in standard format: `<hash> <filename>`
- Reports success/failure for each file

**Always run this after modifying reference data** -- otherwise the next scan will fail integrity verification.

---

## Contributing

1. Fork the repository
Expand Down
1 change: 1 addition & 0 deletions references/known-cves.json.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5b76edfb8d14fe79c3a60d2d7703685df76bd072a01f0a882310e457b587d371 known-cves.json
1 change: 1 addition & 0 deletions references/malicious-patterns.json.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cced9255346755091ddf1d592cddb3185945ad3f2a7f33de3d131b00286ae9d8 malicious-patterns.json
63 changes: 63 additions & 0 deletions scripts/helpers/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,69 @@ get_config_value() {
jq -r "$filter // empty" "$config_file" 2>/dev/null
}

# ─── JSON integrity verification ───────────────────────────────────────────

verify_json_integrity() {
# Usage: verify_json_integrity <json_file_path>
# Returns: 0 if integrity check passes, 1 otherwise
local json_file="$1"
local sha256_file="${json_file}.sha256"

# Check if JSON file exists
if [[ ! -f "$json_file" ]]; then
log_error "JSON file not found: $json_file"
return 1
fi

# Check if checksum file exists
if [[ ! -f "$sha256_file" ]]; then
log_error "Checksum file not found: $sha256_file"
return 1
fi

# Read expected hash from .sha256 file (format: <hash> <filename>)
local expected_hash
if ! read -r expected_hash _ < "$sha256_file" || [[ -z "$expected_hash" ]]; then
log_error "Failed to read checksum from $sha256_file"
return 1
fi
Comment on lines +471 to +476
Copy link

Choose a reason for hiding this comment

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

Checksum file not validated

verify_json_integrity() only reads the first whitespace-delimited field from the .sha256 file and ignores the filename field entirely. If the .sha256 file is malformed or points at a different filename than the JSON being verified, this still passes as long as the first token matches the JSON’s hash. That defeats the “standard format” guarantee documented elsewhere and makes integrity checks easier to accidentally misconfigure (or intentionally confuse) without detection.

Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/helpers/common.sh
Line: 471:476

Comment:
**Checksum file not validated**

`verify_json_integrity()` only reads the first whitespace-delimited field from the `.sha256` file and ignores the filename field entirely. If the `.sha256` file is malformed or points at a different filename than the JSON being verified, this still passes as long as the first token matches the JSON’s hash. That defeats the “standard format” guarantee documented elsewhere and makes integrity checks easier to accidentally misconfigure (or intentionally confuse) without detection.

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


# Compute current hash based on OS
local current_hash
local os
os="$(detect_os)"

if [[ "$os" == "macos" ]]; then
if ! has_cmd shasum; then
log_error "shasum command not found (required on macOS)"
return 1
fi
current_hash="$(shasum -a 256 "$json_file" 2>/dev/null | awk '{print $1}')"
else
# Linux or unknown - try sha256sum
if ! has_cmd sha256sum; then
log_error "sha256sum command not found"
return 1
fi
current_hash="$(sha256sum "$json_file" 2>/dev/null | awk '{print $1}')"
fi

if [[ -z "$current_hash" ]]; then
log_error "Failed to compute hash for $json_file"
return 1
fi

# Compare hashes
if [[ "$current_hash" != "$expected_hash" ]]; then
log_error "Integrity check failed for $json_file"
log_error "Expected: $expected_hash"
log_error "Got: $current_hash"
return 1
fi

return 0
}

# ─── Finding emitter ────────────────────────────────────────────────────────
# Each scanner outputs findings as JSON objects, one per line, collected
# into a JSON array by the orchestrator.
Expand Down
107 changes: 56 additions & 51 deletions scripts/scan_cves.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,58 +87,63 @@ if [[ -n "$CURRENT_VERSION" ]]; then
if [[ ! -f "$KNOWN_CVES_FILE" ]]; then
log_warn "known-cves.json not found at $KNOWN_CVES_FILE -- skipping CVE database checks"
else
# CHK-CVE-001: CVE-2026-25253 -- 1-Click RCE
cve_001_fixed="2026.1.29"
if ! version_gte "$CURRENT_VERSION" "$cve_001_fixed"; then
add_finding \
"CHK-CVE-001" "critical" \
"Vulnerable to CVE-2026-25253: 1-Click RCE via auth token exfiltration" \
"Cross-site WebSocket hijacking. Control UI trusts gatewayUrl from query string, leaking gateway auth token. CVSS 8.8." \
"Installed: $CURRENT_VERSION, fixed in: $cve_001_fixed" \
"Upgrade OpenClaw to >= $cve_001_fixed: npm update -g openclaw"
else
add_finding \
"CHK-CVE-001" "ok" \
"Not vulnerable to CVE-2026-25253" \
"Installed version ($CURRENT_VERSION) includes the fix for CVE-2026-25253." \
"Installed: $CURRENT_VERSION >= $cve_001_fixed" \
""
fi

# CHK-CVE-002: CVE-2026-24763 -- Docker command injection
cve_002_fixed="2026.1.29"
if ! version_gte "$CURRENT_VERSION" "$cve_002_fixed"; then
add_finding \
"CHK-CVE-002" "critical" \
"Vulnerable to CVE-2026-24763: Command injection in Docker sandbox" \
"Unsafe PATH env var handling in shell command construction. CVSS 8.8." \
"Installed: $CURRENT_VERSION, fixed in: $cve_002_fixed" \
"Upgrade OpenClaw to >= $cve_002_fixed: npm update -g openclaw"
# Verify JSON integrity before using
if ! verify_json_integrity "$KNOWN_CVES_FILE"; then # known-cves.json
log_error "Integrity verification failed for known-cves.json -- skipping CVE database checks"
else
add_finding \
"CHK-CVE-002" "ok" \
"Not vulnerable to CVE-2026-24763" \
"Installed version ($CURRENT_VERSION) includes the fix for CVE-2026-24763." \
"Installed: $CURRENT_VERSION >= $cve_002_fixed" \
""
fi

# CHK-CVE-003: CVE-2026-25157 -- SSH command injection
cve_003_fixed="2026.1.29"
if ! version_gte "$CURRENT_VERSION" "$cve_003_fixed"; then
add_finding \
"CHK-CVE-003" "critical" \
"Vulnerable to CVE-2026-25157: OS command injection via SSH project path" \
"Unescaped project root path in sshNodeCommand error echo. CVSS 8.8." \
"Installed: $CURRENT_VERSION, fixed in: $cve_003_fixed" \
"Upgrade OpenClaw to >= $cve_003_fixed: npm update -g openclaw"
else
add_finding \
"CHK-CVE-003" "ok" \
"Not vulnerable to CVE-2026-25157" \
"Installed version ($CURRENT_VERSION) includes the fix for CVE-2026-25157." \
"Installed: $CURRENT_VERSION >= $cve_003_fixed" \
""
# CHK-CVE-001: CVE-2026-25253 -- 1-Click RCE
cve_001_fixed="2026.1.29"
if ! version_gte "$CURRENT_VERSION" "$cve_001_fixed"; then
add_finding \
"CHK-CVE-001" "critical" \
"Vulnerable to CVE-2026-25253: 1-Click RCE via auth token exfiltration" \
"Cross-site WebSocket hijacking. Control UI trusts gatewayUrl from query string, leaking gateway auth token. CVSS 8.8." \
"Installed: $CURRENT_VERSION, fixed in: $cve_001_fixed" \
"Upgrade OpenClaw to >= $cve_001_fixed: npm update -g openclaw"
else
add_finding \
"CHK-CVE-001" "ok" \
"Not vulnerable to CVE-2026-25253" \
"Installed version ($CURRENT_VERSION) includes the fix for CVE-2026-25253." \
"Installed: $CURRENT_VERSION >= $cve_001_fixed" \
""
fi

# CHK-CVE-002: CVE-2026-24763 -- Docker command injection
cve_002_fixed="2026.1.29"
if ! version_gte "$CURRENT_VERSION" "$cve_002_fixed"; then
add_finding \
"CHK-CVE-002" "critical" \
"Vulnerable to CVE-2026-24763: Command injection in Docker sandbox" \
"Unsafe PATH env var handling in shell command construction. CVSS 8.8." \
"Installed: $CURRENT_VERSION, fixed in: $cve_002_fixed" \
"Upgrade OpenClaw to >= $cve_002_fixed: npm update -g openclaw"
else
add_finding \
"CHK-CVE-002" "ok" \
"Not vulnerable to CVE-2026-24763" \
"Installed version ($CURRENT_VERSION) includes the fix for CVE-2026-24763." \
"Installed: $CURRENT_VERSION >= $cve_002_fixed" \
""
fi

# CHK-CVE-003: CVE-2026-25157 -- SSH command injection
cve_003_fixed="2026.1.29"
if ! version_gte "$CURRENT_VERSION" "$cve_003_fixed"; then
add_finding \
"CHK-CVE-003" "critical" \
"Vulnerable to CVE-2026-25157: OS command injection via SSH project path" \
"Unescaped project root path in sshNodeCommand error echo. CVSS 8.8." \
"Installed: $CURRENT_VERSION, fixed in: $cve_003_fixed" \
"Upgrade OpenClaw to >= $cve_003_fixed: npm update -g openclaw"
else
add_finding \
"CHK-CVE-003" "ok" \
"Not vulnerable to CVE-2026-25157" \
"Installed version ($CURRENT_VERSION) includes the fix for CVE-2026-25157." \
"Installed: $CURRENT_VERSION >= $cve_003_fixed" \
""
fi
fi
fi

Expand Down
Loading