From b3452d5e2eef58fab3bd6b6c8beac9c7440a42be Mon Sep 17 00:00:00 2001 From: Black Circle Sentinel Date: Fri, 6 Feb 2026 21:47:43 -0500 Subject: [PATCH 1/8] auto-claude: subtask-1-1 - Create SARIF formatter helper script --- scripts/helpers/sarif.sh | 141 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 scripts/helpers/sarif.sh diff --git a/scripts/helpers/sarif.sh b/scripts/helpers/sarif.sh new file mode 100644 index 0000000..dd352c4 --- /dev/null +++ b/scripts/helpers/sarif.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ─── ClawPinch SARIF formatter ───────────────────────────────────────────── +# Converts ClawPinch findings JSON to SARIF v2.1.0 format for GitHub Code +# Scanning and other static analysis platforms. + +# Ensure common helpers are available +if [[ -z "${_CLAWPINCH_HAS_COLOR:-}" ]]; then + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + # shellcheck source=scripts/helpers/common.sh + source "$SCRIPT_DIR/common.sh" +fi + +# ─── SARIF severity mapping ──────────────────────────────────────────────── + +_sarif_level() { + case "$1" in + critical) echo "error" ;; + warn) echo "warning" ;; + info) echo "note" ;; + ok) echo "none" ;; + *) echo "note" ;; + esac +} + +# ─── SARIF converter ────────────────────────────────────────────────────── +# Usage: convert_to_sarif +# findings_json: JSON array of ClawPinch findings +# +# Outputs: Valid SARIF v2.1.0 JSON to stdout + +convert_to_sarif() { + local findings_json="${1:-[]}" + + # Require jq for SARIF generation + if ! require_cmd jq; then + log_error "jq is required for SARIF output" + return 1 + fi + + # Get tool version from package.json (fallback to 1.2.0) + local tool_version="1.2.0" + local package_json + package_json="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)/package.json" + if [[ -f "$package_json" ]]; then + tool_version="$(jq -r '.version // "1.2.0"' "$package_json" 2>/dev/null || echo "1.2.0")" + fi + + # SARIF schema URL + local sarif_schema="https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json" + + # Tool information URI + local tool_uri="https://github.com/MikeeBuilds/clawpinch" + + # Build SARIF document using jq + echo "$findings_json" | jq -c \ + --arg schema "$sarif_schema" \ + --arg version "2.1.0" \ + --arg tool_name "clawpinch" \ + --arg tool_version "$tool_version" \ + --arg tool_uri "$tool_uri" \ + '{ + "$schema": $schema, + "version": $version, + "runs": [ + { + "tool": { + "driver": { + "name": $tool_name, + "version": $tool_version, + "informationUri": $tool_uri, + "rules": ( + . | map({ + id: .id, + name: .title, + shortDescription: { + text: .title + }, + fullDescription: { + text: .description + }, + helpUri: ($tool_uri + "#" + (.id | ascii_downcase)), + properties: { + category: ( + if .id | startswith("CHK-CFG-") then "configuration" + elif .id | startswith("CHK-SEC-") then "secrets" + elif .id | startswith("CHK-NET-") then "network" + elif .id | startswith("CHK-SKL-") then "skills" + elif .id | startswith("CHK-PRM-") then "permissions" + elif .id | startswith("CHK-CRN-") then "cron" + elif .id | startswith("CHK-CVE-") then "cve" + elif .id | startswith("CHK-SUP-") then "supply-chain" + else "general" + end + ) + } + }) + | unique_by(.id) + ) + } + }, + "results": ( + . | map({ + "ruleId": .id, + "level": ( + if .severity == "critical" then "error" + elif .severity == "warn" then "warning" + elif .severity == "info" then "note" + elif .severity == "ok" then "none" + else "note" + end + ), + "message": { + "text": .title, + "markdown": ( + if .remediation != "" then + (.description + "\n\n**Remediation:** " + .remediation) + else + .description + end + ) + }, + "properties": { + "evidence": .evidence, + "auto_fix": .auto_fix + } + }) + ) + } + ] + }' +} + +# ─── Main ────────────────────────────────────────────────────────────────── +# If this script is executed directly (not sourced), convert stdin to SARIF + +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + findings="$(cat)" + convert_to_sarif "$findings" +fi From 6ee130c844fb5fcefee80324e3b47afc4d7670aa Mon Sep 17 00:00:00 2001 From: Black Circle Sentinel Date: Fri, 6 Feb 2026 21:49:42 -0500 Subject: [PATCH 2/8] auto-claude: subtask-1-2 - Add --sarif flag parsing to main script --- clawpinch.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clawpinch.sh b/clawpinch.sh index 3f45e26..d293ad6 100755 --- a/clawpinch.sh +++ b/clawpinch.sh @@ -23,6 +23,7 @@ trap '_cleanup_animation; exit 130' INT TERM DEEP=0 JSON_OUTPUT=0 +SARIF_OUTPUT=0 SHOW_FIX=0 QUIET=0 NO_INTERACTIVE=0 @@ -39,6 +40,7 @@ Usage: clawpinch [OPTIONS] Options: --deep Run thorough / deep scans --json Output findings as JSON array only + --sarif Output findings in SARIF format --fix Show auto-fix commands in report --quiet Print summary line only --sequential Run scanners sequentially (default is parallel) @@ -60,6 +62,7 @@ while [[ $# -gt 0 ]]; do case "$1" in --deep) DEEP=1; shift ;; --json) JSON_OUTPUT=1; shift ;; + --sarif) SARIF_OUTPUT=1; shift ;; --fix) SHOW_FIX=1; shift ;; --quiet) QUIET=1; shift ;; --sequential) PARALLEL_SCANNERS=0; shift ;; @@ -86,6 +89,7 @@ done export CLAWPINCH_DEEP="$DEEP" export CLAWPINCH_SHOW_FIX="$SHOW_FIX" export CLAWPINCH_CONFIG_DIR="$CONFIG_DIR" +export SARIF_OUTPUT export QUIET # ─── Validate security config (early check for --remediate) ────────────────── From 02356558fbcbe2edce47a306eb476da6b6df7f17 Mon Sep 17 00:00:00 2001 From: Black Circle Sentinel Date: Fri, 6 Feb 2026 21:54:17 -0500 Subject: [PATCH 3/8] auto-claude: subtask-1-3 - Wire SARIF formatter into output section --- clawpinch.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/clawpinch.sh b/clawpinch.sh index d293ad6..e861b02 100755 --- a/clawpinch.sh +++ b/clawpinch.sh @@ -14,6 +14,7 @@ source "$HELPERS_DIR/common.sh" source "$HELPERS_DIR/report.sh" source "$HELPERS_DIR/redact.sh" source "$HELPERS_DIR/interactive.sh" +source "$HELPERS_DIR/sarif.sh" # ─── Signal trap for animation cleanup ────────────────────────────────────── @@ -135,7 +136,7 @@ export OPENCLAW_CONFIG # ─── Banner ────────────────────────────────────────────────────────────────── -if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then +if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then print_header_animated log_info "OS detected: $CLAWPINCH_OS" if [[ -n "$OPENCLAW_CONFIG" ]]; then @@ -273,7 +274,7 @@ else # Record scanner start time _scanner_start="${EPOCHSECONDS:-$(date +%s)}" - if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then + if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then # Print section header for this scanner print_section_header "$scanner_name" @@ -294,7 +295,7 @@ else elif has_cmd python; then output="$(python "$scanner" 2>/dev/null)" || true else - if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then + if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then stop_spinner "$local_name" 0 0 fi log_warn "Skipping $scanner_name (python not found)" @@ -319,7 +320,7 @@ else _scanner_end="${EPOCHSECONDS:-$(date +%s)}" _scanner_elapsed=$(( _scanner_end - _scanner_start )) - if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then + if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then stop_spinner "$local_name" "$local_count" "$_scanner_elapsed" fi done @@ -329,7 +330,7 @@ fi _scan_end="${EPOCHSECONDS:-$(date +%s)}" _scan_elapsed=$(( _scan_end - _scan_start )) -if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then +if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then printf '\n' fi @@ -356,7 +357,10 @@ count_ok="$(echo "$SORTED_FINDINGS" | jq '[.[] | select(.severity == "ok") # ─── Output ────────────────────────────────────────────────────────────────── -if [[ "$JSON_OUTPUT" -eq 1 ]]; then +if [[ "$SARIF_OUTPUT" -eq 1 ]]; then + # SARIF v2.1.0 output (for GitHub Code Scanning, etc.) + convert_to_sarif "$SORTED_FINDINGS" +elif [[ "$JSON_OUTPUT" -eq 1 ]]; then # Pure JSON output (compact for piping efficiency) echo "$SORTED_FINDINGS" | jq -c . else From 0fffd6b57206378b524414a5c60a9f89f0edcaec Mon Sep 17 00:00:00 2001 From: Black Circle Sentinel Date: Fri, 6 Feb 2026 22:02:56 -0500 Subject: [PATCH 4/8] auto-claude: subtask-1-5 - Create GitHub Actions workflow documentation --- docs/github-actions-sarif.md | 416 +++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 docs/github-actions-sarif.md diff --git a/docs/github-actions-sarif.md b/docs/github-actions-sarif.md new file mode 100644 index 0000000..cfe71e6 --- /dev/null +++ b/docs/github-actions-sarif.md @@ -0,0 +1,416 @@ +# GitHub Actions SARIF Integration + +This guide shows you how to integrate ClawPinch with GitHub Code Scanning using SARIF output. This enables security findings to appear inline in pull requests and in the repository's Security tab. + +--- + +## Quick Start + +Add this workflow to `.github/workflows/clawpinch.yml`: + +```yaml +name: ClawPinch Security Scan + +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + schedule: + # Run weekly on Monday at 9am UTC + - cron: '0 9 * * 1' + +permissions: + contents: read + security-events: write # Required for uploading SARIF results + +jobs: + security-scan: + name: ClawPinch Security Audit + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Run ClawPinch security scan + run: | + npx clawpinch --sarif --no-interactive > clawpinch.sarif + continue-on-error: true # Don't fail the build on findings + + - name: Upload SARIF results to GitHub + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: clawpinch.sarif + category: clawpinch +``` + +--- + +## How It Works + +### 1. Running the Scan + +The workflow runs ClawPinch with the `--sarif` and `--no-interactive` flags: + +```bash +npx clawpinch --sarif --no-interactive > clawpinch.sarif +``` + +- `--sarif` produces SARIF v2.1.0 JSON output instead of the standard terminal UI +- `--no-interactive` skips the post-scan menu (required for CI/CD) +- Output is redirected to `clawpinch.sarif` + +### 2. Uploading Results + +The `github/codeql-action/upload-sarif@v3` action uploads the SARIF file to GitHub: + +```yaml +- name: Upload SARIF results to GitHub + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: clawpinch.sarif + category: clawpinch +``` + +**Parameters:** +- `sarif_file`: Path to the SARIF output file +- `category`: Identifies this as a ClawPinch scan (allows multiple analysis tools) + +**Requirements:** +- The workflow must have `security-events: write` permission +- The repository must have GitHub Advanced Security enabled (free for public repos, requires license for private repos) + +### 3. Viewing Results in Pull Requests + +Once uploaded, security findings appear in pull requests: + +- **Code annotations**: Findings appear as inline comments on the relevant files +- **PR checks**: A "Code scanning results / ClawPinch" check appears in the PR status +- **Diff view**: Only findings introduced in the PR are highlighted +- **Severity badges**: Critical, warning, and info findings are color-coded + +**Example PR annotation:** + +``` +┃ ● CRITICAL CHK-CFG-001 ┃ +┃ exec.ask not set to always ┃ +┃ ┃ +┃ The exec.ask setting controls whether the user is ┃ +┃ prompted before command execution. ┃ +┃ ┃ +┃ Fix: Set exec.ask to 'always' in openclaw.json ┃ +``` + +### 4. Viewing Results in the Security Tab + +All findings across all scans are tracked in the repository's Security tab: + +1. Navigate to your repository → **Security** tab +2. Click **Code scanning** in the left sidebar +3. Filter by tool: **ClawPinch** + +**Features:** +- **Timeline view**: Track when findings were introduced and fixed +- **Trend analysis**: See security posture improving over time +- **Filter by severity**: Focus on critical findings first +- **Dismissal workflow**: Mark findings as false positives or won't-fix with comments + +--- + +## Advanced Configuration + +### Run on Specific Directories + +If your OpenClaw deployment is in a subdirectory: + +```yaml +- name: Run ClawPinch security scan + run: | + npx clawpinch --sarif --no-interactive --config-dir ./infra/openclaw > clawpinch.sarif +``` + +### Deep Scan Mode + +Enable supply-chain verification and skill decompilation: + +```yaml +- name: Run ClawPinch deep scan + run: | + npx clawpinch --sarif --no-interactive --deep > clawpinch.sarif +``` + +### Fail Build on Critical Findings + +By default, `continue-on-error: true` prevents failing the build. To enforce a security gate: + +```yaml +- name: Run ClawPinch security scan + run: | + npx clawpinch --sarif --no-interactive > clawpinch.sarif + + # Check if any critical findings exist + if jq -e '.runs[0].results[] | select(.level == "error")' clawpinch.sarif > /dev/null; then + echo "❌ Critical security findings detected" + exit 1 + fi +``` + +### Multiple SARIF Uploads + +If you run multiple scans (e.g., different environments), use unique categories: + +```yaml +- name: Scan production config + run: npx clawpinch --sarif --config-dir ./prod > clawpinch-prod.sarif + +- name: Upload production results + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: clawpinch-prod.sarif + category: clawpinch-prod + +- name: Scan staging config + run: npx clawpinch --sarif --config-dir ./staging > clawpinch-staging.sarif + +- name: Upload staging results + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: clawpinch-staging.sarif + category: clawpinch-staging +``` + +--- + +## Combining with Other Scanners + +SARIF allows you to aggregate results from multiple static analysis tools: + +```yaml +name: Security Scanning Suite + +jobs: + security-scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # ClawPinch for OpenClaw-specific checks + - run: npx clawpinch --sarif > clawpinch.sarif + - uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: clawpinch.sarif + category: clawpinch + + # Semgrep for general code patterns + - run: semgrep --sarif > semgrep.sarif + - uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: semgrep.sarif + category: semgrep + + # Trivy for container scanning + - run: trivy image --format sarif myapp:latest > trivy.sarif + - uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy.sarif + category: trivy +``` + +All findings appear together in the Security tab, filterable by tool. + +--- + +## Troubleshooting + +### "Resource not accessible by integration" + +**Error:** +``` +Error: Resource not accessible by integration +``` + +**Solution:** +Add `security-events: write` permission to the workflow: + +```yaml +permissions: + contents: read + security-events: write +``` + +### "Advanced Security must be enabled" + +**Error:** +``` +Advanced Security must be enabled for this repository to use code scanning. +``` + +**Solution:** +- For **public repositories**: GitHub Advanced Security is free and automatically available +- For **private repositories**: Enable GitHub Advanced Security in repository settings (requires GitHub Enterprise license) + +### Invalid SARIF File + +**Error:** +``` +Error: Invalid SARIF. The SARIF file is not valid. +``` + +**Solution:** +Validate the SARIF file before uploading: + +```yaml +- name: Validate SARIF output + run: | + # Install SARIF validator + npm install -g @microsoft/sarif-multitool + + # Validate against SARIF v2.1.0 schema + sarif-multitool validate clawpinch.sarif +``` + +If validation fails, [open an issue](https://github.com/MikeeBuilds/clawpinch/issues) with the output. + +### No Findings Appear in PRs + +**Checklist:** +1. Verify the workflow completed successfully in the Actions tab +2. Check that `security-events: write` permission is set +3. Ensure the SARIF file was uploaded (check action logs) +4. Wait 1-2 minutes for GitHub to process the SARIF file +5. Verify findings are for files changed in the PR (GitHub only shows diff-related alerts in PR checks) + +--- + +## SARIF Output Format Reference + +ClawPinch produces SARIF v2.1.0 output with the following structure: + +```json +{ + "version": "2.1.0", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "runs": [{ + "tool": { + "driver": { + "name": "ClawPinch", + "version": "1.2.0", + "informationUri": "https://github.com/MikeeBuilds/clawpinch", + "rules": [ + { + "id": "CHK-CFG-001", + "name": "GatewayListeningOnAllInterfaces", + "shortDescription": { + "text": "Gateway listening on 0.0.0.0" + }, + "helpUri": "https://github.com/MikeeBuilds/clawpinch#chk-cfg-001", + "defaultConfiguration": { + "level": "error" + } + } + ] + } + }, + "results": [ + { + "ruleId": "CHK-CFG-001", + "level": "error", + "message": { + "text": "Gateway listening on 0.0.0.0 - restricts to localhost (127.0.0.1)", + "markdown": "**Finding:** Gateway listening on 0.0.0.0\n\n**Fix:** Set gateway.host to '127.0.0.1' in openclaw.json" + } + } + ] + }] +} +``` + +**Severity Mapping:** +- `critical` → SARIF `error` +- `warn` → SARIF `warning` +- `info` → SARIF `note` +- `ok` → Not included in SARIF output (only findings) + +--- + +## Related Documentation + +- [ClawPinch README](../README.md) — Installation and usage +- [Check Catalog](../references/check-catalog.md) — Full list of 63 checks +- [SARIF Specification](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html) — Official SARIF v2.1.0 spec +- [GitHub Code Scanning](https://docs.github.com/en/code-security/code-scanning) — GitHub Advanced Security docs + +--- + +## Example: Complete CI/CD Pipeline + +This workflow runs ClawPinch on every PR, uploads results to GitHub, and blocks merging on critical findings: + +```yaml +name: Security Gate + +on: + pull_request: + branches: [main] + +permissions: + contents: read + security-events: write + pull-requests: write + +jobs: + clawpinch-scan: + name: ClawPinch Security Scan + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run ClawPinch + id: scan + run: | + npx clawpinch --sarif --no-interactive > clawpinch.sarif + + # Count critical findings + CRITICAL=$(jq '[.runs[0].results[] | select(.level == "error")] | length' clawpinch.sarif) + echo "critical=$CRITICAL" >> $GITHUB_OUTPUT + continue-on-error: true + + - name: Upload SARIF to GitHub + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: clawpinch.sarif + category: clawpinch + + - name: Comment on PR + uses: actions/github-script@v7 + with: + script: | + const critical = ${{ steps.scan.outputs.critical }}; + const body = critical > 0 + ? `⛔ **ClawPinch found ${critical} critical security finding(s)**\n\nReview the Code Scanning alerts for details.` + : `✅ **ClawPinch scan passed** - No critical findings`; + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: body + }); + + - name: Enforce security gate + if: steps.scan.outputs.critical > 0 + run: | + echo "❌ Blocking merge: ${{ steps.scan.outputs.critical }} critical findings detected" + exit 1 +``` + +This enforces a security gate — PRs with critical findings cannot be merged until they're fixed. + +--- + +## License + +MIT From ec337393127eef6186ec6db22e70715e3ed91f25 Mon Sep 17 00:00:00 2001 From: Black Circle Sentinel Date: Sun, 8 Feb 2026 21:39:58 -0500 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20address=20review=20feedback=20?= =?UTF-8?q?=E2=80=94=20filter=20ok=20from=20SARIF,=20remove=20dead=20code,?= =?UTF-8?q?=20add=20locations,=20refactor=20UI=20condition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- clawpinch.sh | 21 +++++++++++++-------- scripts/helpers/sarif.sh | 26 +++++++++++--------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/clawpinch.sh b/clawpinch.sh index e861b02..d05189a 100755 --- a/clawpinch.sh +++ b/clawpinch.sh @@ -90,9 +90,14 @@ done export CLAWPINCH_DEEP="$DEEP" export CLAWPINCH_SHOW_FIX="$SHOW_FIX" export CLAWPINCH_CONFIG_DIR="$CONFIG_DIR" -export SARIF_OUTPUT export QUIET +# Determine if we should show terminal UI (not in JSON/SARIF/quiet mode) +_SHOW_UI=1 +if [[ "$JSON_OUTPUT" -eq 1 ]] || [[ "$SARIF_OUTPUT" -eq 1 ]] || [[ "$QUIET" -eq 1 ]]; then + _SHOW_UI=0 +fi + # ─── Validate security config (early check for --remediate) ────────────────── # Fail fast with a clear setup message instead of per-command failures later. @@ -136,7 +141,7 @@ export OPENCLAW_CONFIG # ─── Banner ────────────────────────────────────────────────────────────────── -if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then +if [[ "$_SHOW_UI" -eq 1 ]]; then print_header_animated log_info "OS detected: $CLAWPINCH_OS" if [[ -n "$OPENCLAW_CONFIG" ]]; then @@ -245,7 +250,7 @@ _scan_start="${EPOCHSECONDS:-$(date +%s)}" if [[ "$PARALLEL_SCANNERS" -eq 1 ]]; then # Parallel execution - if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then + if [[ "$_SHOW_UI" -eq 1 ]]; then start_spinner "Running ${scanner_count} scanners in parallel..." fi @@ -261,7 +266,7 @@ if [[ "$PARALLEL_SCANNERS" -eq 1 ]]; then # Count findings from merged results _parallel_count="$(echo "$ALL_FINDINGS" | jq 'length')" - if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then + if [[ "$_SHOW_UI" -eq 1 ]]; then stop_spinner "Parallel scan" "$_parallel_count" "$_parallel_elapsed" fi else @@ -274,7 +279,7 @@ else # Record scanner start time _scanner_start="${EPOCHSECONDS:-$(date +%s)}" - if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then + if [[ "$_SHOW_UI" -eq 1 ]]; then # Print section header for this scanner print_section_header "$scanner_name" @@ -295,7 +300,7 @@ else elif has_cmd python; then output="$(python "$scanner" 2>/dev/null)" || true else - if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then + if [[ "$_SHOW_UI" -eq 1 ]]; then stop_spinner "$local_name" 0 0 fi log_warn "Skipping $scanner_name (python not found)" @@ -320,7 +325,7 @@ else _scanner_end="${EPOCHSECONDS:-$(date +%s)}" _scanner_elapsed=$(( _scanner_end - _scanner_start )) - if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then + if [[ "$_SHOW_UI" -eq 1 ]]; then stop_spinner "$local_name" "$local_count" "$_scanner_elapsed" fi done @@ -330,7 +335,7 @@ fi _scan_end="${EPOCHSECONDS:-$(date +%s)}" _scan_elapsed=$(( _scan_end - _scan_start )) -if [[ "$JSON_OUTPUT" -eq 0 ]] && [[ "$SARIF_OUTPUT" -eq 0 ]] && [[ "$QUIET" -eq 0 ]]; then +if [[ "$_SHOW_UI" -eq 1 ]]; then printf '\n' fi diff --git a/scripts/helpers/sarif.sh b/scripts/helpers/sarif.sh index dd352c4..01b3062 100644 --- a/scripts/helpers/sarif.sh +++ b/scripts/helpers/sarif.sh @@ -12,18 +12,6 @@ if [[ -z "${_CLAWPINCH_HAS_COLOR:-}" ]]; then source "$SCRIPT_DIR/common.sh" fi -# ─── SARIF severity mapping ──────────────────────────────────────────────── - -_sarif_level() { - case "$1" in - critical) echo "error" ;; - warn) echo "warning" ;; - info) echo "note" ;; - ok) echo "none" ;; - *) echo "note" ;; - esac -} - # ─── SARIF converter ────────────────────────────────────────────────────── # Usage: convert_to_sarif # findings_json: JSON array of ClawPinch findings @@ -71,7 +59,7 @@ convert_to_sarif() { "version": $tool_version, "informationUri": $tool_uri, "rules": ( - . | map({ + . | [.[] | select(.severity != "ok")] | map({ id: .id, name: .title, shortDescription: { @@ -101,13 +89,12 @@ convert_to_sarif() { } }, "results": ( - . | map({ + . | [.[] | select(.severity != "ok")] | map({ "ruleId": .id, "level": ( if .severity == "critical" then "error" elif .severity == "warn" then "warning" elif .severity == "info" then "note" - elif .severity == "ok" then "none" else "note" end ), @@ -121,6 +108,15 @@ convert_to_sarif() { end ) }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "." + } + } + } + ], "properties": { "evidence": .evidence, "auto_fix": .auto_fix From b9a8d2c11f6a0122554437004828a08e9fd95156 Mon Sep 17 00:00:00 2001 From: Black Circle Sentinel Date: Sun, 8 Feb 2026 21:54:29 -0500 Subject: [PATCH 6/8] =?UTF-8?q?fix:=20address=20round=202=20review=20?= =?UTF-8?q?=E2=80=94=20fix=20helpUri,=20add=20defaultConfiguration.level?= =?UTF-8?q?=20to=20SARIF=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- docs/github-actions-sarif.md | 2 +- scripts/helpers/sarif.sh | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/github-actions-sarif.md b/docs/github-actions-sarif.md index cfe71e6..e8c94ce 100644 --- a/docs/github-actions-sarif.md +++ b/docs/github-actions-sarif.md @@ -304,7 +304,7 @@ ClawPinch produces SARIF v2.1.0 output with the following structure: "shortDescription": { "text": "Gateway listening on 0.0.0.0" }, - "helpUri": "https://github.com/MikeeBuilds/clawpinch#chk-cfg-001", + "helpUri": "https://github.com/MikeeBuilds/clawpinch/blob/main/references/check-catalog.md#chk-cfg-001", "defaultConfiguration": { "level": "error" } diff --git a/scripts/helpers/sarif.sh b/scripts/helpers/sarif.sh index 01b3062..3618899 100644 --- a/scripts/helpers/sarif.sh +++ b/scripts/helpers/sarif.sh @@ -68,7 +68,16 @@ convert_to_sarif() { fullDescription: { text: .description }, - helpUri: ($tool_uri + "#" + (.id | ascii_downcase)), + helpUri: ($tool_uri + "/blob/main/references/check-catalog.md#" + (.id | ascii_downcase)), + defaultConfiguration: { + level: ( + if .severity == "critical" then "error" + elif .severity == "warn" then "warning" + elif .severity == "info" then "note" + else "note" + end + ) + }, properties: { category: ( if .id | startswith("CHK-CFG-") then "configuration" From 4a83c29e5e8f1ea4cc22d5cd6d2132085cc1c47e Mon Sep 17 00:00:00 2001 From: Black Circle Sentinel Date: Sun, 8 Feb 2026 23:08:07 -0500 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20address=20round=203=20review=20?= =?UTF-8?q?=E2=80=94=20version=20fallback,=20helpUri,=20jq=20optional=20ch?= =?UTF-8?q?aining?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update hardcoded version fallback from 1.2.0 to 1.2.1 to match package.json - Remove broken ascii_downcase fragment anchor from helpUri, point to check-catalog.md without fragment - Use jq optional chaining (.runs[0]?.results[]?) in CI examples for robustness when SARIF has empty/missing runs - Update example SARIF version and helpUri in docs to match code changes - Greptile comment #7 (defaultConfiguration.level) already addressed in round 2 Co-Authored-By: Claude Opus 4.6 --- docs/github-actions-sarif.md | 8 ++++---- scripts/helpers/sarif.sh | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/github-actions-sarif.md b/docs/github-actions-sarif.md index e8c94ce..21c3f17 100644 --- a/docs/github-actions-sarif.md +++ b/docs/github-actions-sarif.md @@ -150,7 +150,7 @@ By default, `continue-on-error: true` prevents failing the build. To enforce a s npx clawpinch --sarif --no-interactive > clawpinch.sarif # Check if any critical findings exist - if jq -e '.runs[0].results[] | select(.level == "error")' clawpinch.sarif > /dev/null; then + if jq -e '.runs[0]?.results[]? | select(.level == "error")' clawpinch.sarif > /dev/null; then echo "❌ Critical security findings detected" exit 1 fi @@ -295,7 +295,7 @@ ClawPinch produces SARIF v2.1.0 output with the following structure: "tool": { "driver": { "name": "ClawPinch", - "version": "1.2.0", + "version": "1.2.1", "informationUri": "https://github.com/MikeeBuilds/clawpinch", "rules": [ { @@ -304,7 +304,7 @@ ClawPinch produces SARIF v2.1.0 output with the following structure: "shortDescription": { "text": "Gateway listening on 0.0.0.0" }, - "helpUri": "https://github.com/MikeeBuilds/clawpinch/blob/main/references/check-catalog.md#chk-cfg-001", + "helpUri": "https://github.com/MikeeBuilds/clawpinch/blob/main/references/check-catalog.md", "defaultConfiguration": { "level": "error" } @@ -374,7 +374,7 @@ jobs: npx clawpinch --sarif --no-interactive > clawpinch.sarif # Count critical findings - CRITICAL=$(jq '[.runs[0].results[] | select(.level == "error")] | length' clawpinch.sarif) + CRITICAL=$(jq '[.runs[0]?.results[]? | select(.level == "error")] | length' clawpinch.sarif) echo "critical=$CRITICAL" >> $GITHUB_OUTPUT continue-on-error: true diff --git a/scripts/helpers/sarif.sh b/scripts/helpers/sarif.sh index 3618899..3293fd7 100644 --- a/scripts/helpers/sarif.sh +++ b/scripts/helpers/sarif.sh @@ -27,12 +27,12 @@ convert_to_sarif() { return 1 fi - # Get tool version from package.json (fallback to 1.2.0) - local tool_version="1.2.0" + # Get tool version from package.json (fallback to 1.2.1) + local tool_version="1.2.1" local package_json package_json="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)/package.json" if [[ -f "$package_json" ]]; then - tool_version="$(jq -r '.version // "1.2.0"' "$package_json" 2>/dev/null || echo "1.2.0")" + tool_version="$(jq -r '.version // "1.2.1"' "$package_json" 2>/dev/null || echo "1.2.1")" fi # SARIF schema URL @@ -68,7 +68,7 @@ convert_to_sarif() { fullDescription: { text: .description }, - helpUri: ($tool_uri + "/blob/main/references/check-catalog.md#" + (.id | ascii_downcase)), + helpUri: ($tool_uri + "/blob/main/references/check-catalog.md"), defaultConfiguration: { level: ( if .severity == "critical" then "error" From f5a64554864dd3fa909c22cf82c60fdca1d12d1a Mon Sep 17 00:00:00 2001 From: Black Circle Sentinel Date: Tue, 10 Feb 2026 09:41:04 -0500 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20address=20review=20=E2=80=94=20corre?= =?UTF-8?q?ct=20docs=20to=20match=20actual=20SARIF=20output=20behavior?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update PR results section: findings appear in Security tab as repository-level alerts, not as inline code annotations, because the SARIF location uses repository root ("uri": ".") without file/line info - Replace hardcoded version "1.2.1" with placeholder and note it is dynamically read from package.json at runtime - Fix example message object to match sarif.sh output: "text" holds the short finding title, "markdown" holds description + remediation in the format (.description + "\n\n**Remediation:** " + .remediation) Co-Authored-By: Claude Opus 4.6 --- docs/github-actions-sarif.md | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/docs/github-actions-sarif.md b/docs/github-actions-sarif.md index 21c3f17..315a16b 100644 --- a/docs/github-actions-sarif.md +++ b/docs/github-actions-sarif.md @@ -1,6 +1,6 @@ # GitHub Actions SARIF Integration -This guide shows you how to integrate ClawPinch with GitHub Code Scanning using SARIF output. This enables security findings to appear inline in pull requests and in the repository's Security tab. +This guide shows you how to integrate ClawPinch with GitHub Code Scanning using SARIF output. This enables security findings to appear in the repository's Security tab as code scanning alerts. --- @@ -83,24 +83,13 @@ The `github/codeql-action/upload-sarif@v3` action uploads the SARIF file to GitH ### 3. Viewing Results in Pull Requests -Once uploaded, security findings appear in pull requests: +Once uploaded, security findings appear in the repository's **Security tab** under Code Scanning: -- **Code annotations**: Findings appear as inline comments on the relevant files +- **Security tab alerts**: Findings are listed as code scanning alerts, filterable by tool ("ClawPinch") - **PR checks**: A "Code scanning results / ClawPinch" check appears in the PR status -- **Diff view**: Only findings introduced in the PR are highlighted - **Severity badges**: Critical, warning, and info findings are color-coded -**Example PR annotation:** - -``` -┃ ● CRITICAL CHK-CFG-001 ┃ -┃ exec.ask not set to always ┃ -┃ ┃ -┃ The exec.ask setting controls whether the user is ┃ -┃ prompted before command execution. ┃ -┃ ┃ -┃ Fix: Set exec.ask to 'always' in openclaw.json ┃ -``` +> **Note:** Because ClawPinch scans runtime configurations rather than source files, findings use the repository root as their location (`"uri": "."`) and do not include specific file or line information. As a result, findings appear in the Security tab as repository-level alerts rather than as inline PR annotations on specific lines of code. ### 4. Viewing Results in the Security Tab @@ -285,7 +274,7 @@ If validation fails, [open an issue](https://github.com/MikeeBuilds/clawpinch/is ## SARIF Output Format Reference -ClawPinch produces SARIF v2.1.0 output with the following structure: +ClawPinch produces SARIF v2.1.0 output with the following structure. The `version` field is dynamically populated from `package.json` at runtime: ```json { @@ -295,7 +284,7 @@ ClawPinch produces SARIF v2.1.0 output with the following structure: "tool": { "driver": { "name": "ClawPinch", - "version": "1.2.1", + "version": "", "informationUri": "https://github.com/MikeeBuilds/clawpinch", "rules": [ { @@ -317,8 +306,8 @@ ClawPinch produces SARIF v2.1.0 output with the following structure: "ruleId": "CHK-CFG-001", "level": "error", "message": { - "text": "Gateway listening on 0.0.0.0 - restricts to localhost (127.0.0.1)", - "markdown": "**Finding:** Gateway listening on 0.0.0.0\n\n**Fix:** Set gateway.host to '127.0.0.1' in openclaw.json" + "text": "Gateway listening on 0.0.0.0", + "markdown": "The gateway is configured to listen on all interfaces (0.0.0.0), exposing it to the network.\n\n**Remediation:** Set gateway.host to '127.0.0.1' in openclaw.json" } } ]