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
53 changes: 53 additions & 0 deletions .clawpinch-ignore.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"$schema": "https://clawpinch.dev/schemas/ignore.json",
"_comment": "This file configures finding suppression for ClawPinch security scans",
"_documentation": {
"purpose": "Suppress accepted-risk findings to prevent CI/CD pipeline failures",
"location": "Copy this file to .clawpinch-ignore.json in your project root",
"fields": {
"id": "(required) Check ID to suppress (e.g., CHK-CFG-001)",
"reason": "(required) Justification for suppressing this finding",
"expires": "(optional) ISO 8601 timestamp when suppression expires and finding reactivates",
"suppressed_by": "(optional) Email or identifier of person who approved suppression",
"suppressed_at": "(optional) ISO 8601 timestamp when suppression was created"
},
"behavior": {
"suppressed_findings": "Moved to 'suppressed' array in JSON output, excluded from severity counts",
"expired_suppressions": "Automatically reactivated if expires date is in the past",
"flags": {
"--show-suppressed": "Include suppressed findings in output with [SUPPRESSED] marker",
"--no-ignore": "Disable all suppressions for full audit scans"
}
}
},
"suppressions": [
{
"id": "CHK-CFG-001",
"reason": "Dev environment - open gateway is intentional for local testing",
"expires": "2025-12-31T23:59:59Z",
"suppressed_by": "devops@example.com",
"suppressed_at": "2024-01-15T10:30:00Z"
},
{
"id": "CHK-SEC-003",
"reason": "Test API key in example config - not used in production deployments"
},
{
"id": "CHK-NET-004",
"reason": "WebSocket endpoint exposed for real-time collaboration feature",
"expires": "2025-06-30T00:00:00Z",
"suppressed_by": "security-team@example.com"
},
{
"id": "CHK-PRM-002",
"reason": "Wildcard permission required for dynamic skill loader - reviewed by security",
"suppressed_by": "ciso@example.com",
"suppressed_at": "2024-02-20T14:30:00Z"
},
{
"id": "CHK-CRN-001",
"reason": "Cron job needs elevated permissions for system maintenance tasks",
"expires": "2025-03-01T00:00:00Z"
}
]
}
54 changes: 54 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ bash clawpinch.sh --remediate

# Deep scan
bash clawpinch.sh --deep

# Show suppressed findings
bash clawpinch.sh --show-suppressed

# Disable all suppressions (full audit)
bash clawpinch.sh --no-ignore
```

## Architecture
Expand All @@ -32,6 +38,7 @@ clawpinch/
│ │ ├── report.sh # Terminal UI: banner, finding cards, summary dashboard, spinner
│ │ ├── redact.sh # Secret redaction: redact_value(), redact_line(), redact_json_secrets()
│ │ ├── safe_exec.sh # Safe command execution: whitelist-based validation, replaces eval()
│ │ ├── suppression.sh # Finding suppression: load_suppressions(), filter_findings(), expiration handling
│ │ └── interactive.sh # Post-scan menu: review, auto-fix, handoff export, AI remediation
│ ├── scan_config.sh # CHK-CFG-001..010 — gateway, TLS, auth, CORS
│ ├── scan_secrets.py # CHK-SEC-001..008 — API keys, passwords, tokens
Expand All @@ -46,6 +53,7 @@ clawpinch/
│ ├── malicious-patterns.json # Known bad skill hashes
│ ├── check-catalog.md # Full check documentation
│ └── threat-model.md # Threat model for OpenClaw
├── .clawpinch-ignore.json.example # Example suppression config with documentation
├── package.json # npm package metadata
├── SKILL.md # AI-readable skill documentation
├── CLAUDE.md # This file — project context for Claude Code
Expand All @@ -72,6 +80,19 @@ Every scanner emits findings via `emit_finding()` from `common.sh`:

Severity order: `critical` > `warn` > `info` > `ok`.

### Output Format with Suppressions

When suppressions are enabled (via `.clawpinch-ignore.json`), the JSON output contains two arrays:

```json
{
"findings": [ /* active findings only */ ],
"suppressed": [ /* suppressed findings */ ]
}
```

Suppressed findings do not count toward severity totals or exit codes. Use `--show-suppressed` to include them in terminal output (marked with `[SUPPRESSED]`), or `--no-ignore` to disable all suppressions for full audits.

## How to Add a New Check

1. Choose the appropriate scanner file in `scripts/` (or create a new `scan_*.sh`)
Expand All @@ -80,6 +101,39 @@ Severity order: `critical` > `warn` > `info` > `ok`.
4. Call `emit_finding "CHK-XXX-NNN" "severity" "title" "description" "evidence" "remediation" "auto_fix"`
5. Add the check to `references/check-catalog.md` and `SKILL.md` category table

## Finding Suppression

Findings can be suppressed by creating a `.clawpinch-ignore.json` file in the project root:

```json
{
"suppressions": [
{
"id": "CHK-CFG-001",
"reason": "Dev environment - open gateway is intentional",
"expires": "2025-12-31T23:59:59Z",
"suppressed_by": "devops@example.com",
"suppressed_at": "2024-01-15T10:30:00Z"
}
]
}
```

**Behavior:**
- Suppressed findings move to `suppressed` array in JSON output
- Suppressed findings do not count toward severity totals or exit codes
- Expired suppressions (past `expires` date) automatically reactivate
- `--show-suppressed` includes suppressed findings in output with `[SUPPRESSED]` marker
- `--no-ignore` disables all suppressions for full audit scans

**Use cases:**
- Accepted risks in development environments
- Findings under gradual remediation with expiration tracking
- Security-reviewed exceptions with documented justifications
- CI/CD pipelines that fail on active findings only

See `.clawpinch-ignore.json.example` for a fully documented template.

## Conventions

- All scanners output a JSON array to stdout
Expand Down
128 changes: 128 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ bash clawpinch.sh --config-dir /path/to/openclaw/config

# Print auto-fix commands (read-only -- does not execute them)
bash clawpinch.sh --fix

# Show suppressed findings in output
bash clawpinch.sh --show-suppressed

# Disable all suppressions for full audit
bash clawpinch.sh --no-ignore
```

---
Expand Down Expand Up @@ -241,6 +247,128 @@ bash clawpinch.sh --sequential

---

## Suppressing Findings

ClawPinch allows you to suppress specific findings by check ID -- useful for accepted risks in development environments or findings that have been reviewed and approved by your security team. Suppressed findings are still scanned but reported separately and excluded from severity counts and exit codes.

### Creating a Suppression File

Create a `.clawpinch-ignore.json` file in your project root:

```json
{
"suppressions": [
{
"id": "CHK-CFG-001",
"reason": "Dev environment - open gateway is intentional for local testing"
},
{
"id": "CHK-SEC-003",
"reason": "Test API key in example config - not used in production",
"expires": "2025-12-31T23:59:59Z",
"suppressed_by": "security-team@example.com"
}
]
}
```

### Suppression Fields

- **`id`** (required) -- The check ID to suppress (e.g., `CHK-CFG-001`, `CHK-NET-004`)
- **`reason`** (required) -- Justification for why this finding is being suppressed
- **`expires`** (optional) -- ISO 8601 timestamp when the suppression expires and the finding reactivates
- **`suppressed_by`** (optional) -- Email or identifier of the person who approved the suppression
- **`suppressed_at`** (optional) -- ISO 8601 timestamp when the suppression was created

### How Suppressions Work

1. **Automatic Filtering** -- Suppressed findings are moved from the main `findings` array to a separate `suppressed` array in JSON output
2. **Severity Exclusion** -- Suppressed findings do not count toward severity totals or affect exit codes
3. **Expiration** -- If a suppression has an `expires` date in the past, it is automatically reactivated and the finding appears in normal output
4. **Visibility** -- Use `--show-suppressed` to include suppressed findings in output (marked with `[SUPPRESSED]`)
5. **Audit Mode** -- Use `--no-ignore` to disable all suppressions for full audit scans

### Example Workflow

```bash
# 1. Run initial scan and identify accepted risks
npx clawpinch --json > findings.json

# 2. Create .clawpinch-ignore.json with suppressions
cat > .clawpinch-ignore.json <<EOF
{
"suppressions": [
{
"id": "CHK-CFG-001",
"reason": "Dev environment - reviewed by security team",
"expires": "2025-12-31T23:59:59Z",
"suppressed_by": "devops@example.com"
}
]
}
EOF

# 3. Re-run scan -- suppressed findings no longer fail CI
npx clawpinch --json
# Exit code: 0 (even if CHK-CFG-001 was critical)

# 4. Review suppressed findings
npx clawpinch --show-suppressed

# 5. Full audit (ignore all suppressions)
npx clawpinch --no-ignore
```

### JSON Output with Suppressions

When suppressions are configured, JSON output includes both `findings` and `suppressed` arrays:

```json
{
"findings": [
{
"id": "CHK-CFG-002",
"severity": "warn",
"title": "Debug mode enabled",
"description": "...",
"evidence": "...",
"remediation": "...",
"auto_fix": ""
}
],
"suppressed": [
{
"id": "CHK-CFG-001",
"severity": "critical",
"title": "Gateway listening on 0.0.0.0",
"description": "...",
"evidence": "...",
"remediation": "...",
"auto_fix": "",
"suppression": {
"reason": "Dev environment - reviewed by security team",
"expires": "2025-12-31T23:59:59Z",
"suppressed_by": "devops@example.com"
}
Comment on lines +348 to +352
Copy link

Choose a reason for hiding this comment

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

Documentation shows suppression object in JSON output, but filter_findings() at scripts/helpers/suppression.sh:173-185 doesn't add it - just copies findings as-is.

Either enrich findings with metadata or update docs to reflect actual output format.

Prompt To Fix With AI
This is a comment left during a code review.
Path: README.md
Line: 348:352

Comment:
Documentation shows `suppression` object in JSON output, but `filter_findings()` at `scripts/helpers/suppression.sh:173-185` doesn't add it - just copies findings as-is. 

Either enrich findings with metadata or update docs to reflect actual output format.

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

}
]
}
```

### Use Cases

- **CI/CD Pipelines** -- Suppress accepted risks to prevent false positive failures
- **Development Environments** -- Suppress intentional misconfigurations (e.g., open gateways for debugging)
- **Gradual Remediation** -- Suppress findings while you work on fixes, set expiration dates to ensure follow-up
- **Security Review** -- Document accepted risks with justifications and approval metadata
- **Quarterly Audits** -- Use `--no-ignore` to perform full audits that include all suppressed findings

### Example File

See `.clawpinch-ignore.json.example` in the repository for a fully documented template with multiple suppression examples.

---

## Example Output

```
Expand Down
64 changes: 62 additions & 2 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ clawpinch --no-interactive
# AI-powered remediation — scan then pipe to Claude for automated fixing
clawpinch --remediate

# Show suppressed findings in output (marked with [SUPPRESSED])
clawpinch --show-suppressed

# Disable all suppressions for full audit scans
clawpinch --no-ignore

# Target specific config directory
clawpinch --config-dir /path/to/openclaw/config

Expand All @@ -84,6 +90,17 @@ Each finding is a JSON object:
}
```

When suppressions are enabled via `.clawpinch-ignore.json`, the output format changes:

```json
{
"findings": [ /* active findings only */ ],
"suppressed": [ /* suppressed findings */ ]
}
```

Suppressed findings do not count toward severity totals or exit codes.

## Check Categories

| Category | ID Range | Count | Description |
Expand Down Expand Up @@ -121,6 +138,47 @@ npx clawpinch --json --no-interactive | jq '[.[] | select(.severity == "critical
npx clawpinch --quiet --no-interactive
```

## Finding Suppression

Suppress specific findings by creating `.clawpinch-ignore.json`:

```json
{
"suppressions": [
{
"id": "CHK-CFG-001",
"reason": "Dev environment - open gateway is intentional",
"expires": "2025-12-31T23:59:59Z",
"suppressed_by": "devops@example.com",
"suppressed_at": "2024-01-15T10:30:00Z"
}
]
}
```

**Fields:**
- `id` (required): Check ID to suppress
- `reason` (required): Justification for suppression
- `expires` (optional): ISO 8601 timestamp when suppression expires
- `suppressed_by` (optional): Email/identifier of approver
- `suppressed_at` (optional): ISO 8601 timestamp when created

**Behavior:**
- Suppressed findings appear in `suppressed` array, not `findings`
- Suppressed findings excluded from severity counts and exit codes
- Expired suppressions automatically reactivate
- `--show-suppressed`: Include suppressed findings in output with `[SUPPRESSED]` marker
- `--no-ignore`: Disable all suppressions for full audits

**Use cases:**
- **CI/CD:** Prevent pipeline failures from accepted risks
- **Development:** Suppress intentional misconfigurations in dev environments
- **Gradual remediation:** Suppress findings with expiration dates for periodic review
- **Security review:** Document accepted risks with justifications
- **Audits:** Use `--no-ignore` for quarterly full scans

See `.clawpinch-ignore.json.example` for detailed examples.

## Dependencies

- **Required:** `bash` >= 4.0, `jq`
Expand All @@ -139,5 +197,7 @@ npx clawpinch --quiet --no-interactive

| Code | Meaning |
|------|---------|
| 0 | No critical findings |
| 1 | One or more critical findings detected |
| 0 | No active critical findings (suppressed findings excluded) |
| 1 | One or more active critical findings detected |

**Note:** Suppressed findings do not affect exit codes. Use `--no-ignore` to include suppressed findings in exit code calculation.
Loading