Skip to content

feat(SIG-107): add OpenGrep and Brakeman runner tools - #30

Merged
damartinezjulio merged 1 commit into
mainfrom
sig-107
Jun 12, 2026
Merged

feat(SIG-107): add OpenGrep and Brakeman runner tools#30
damartinezjulio merged 1 commit into
mainfrom
sig-107

Conversation

@damartinezjulio

@damartinezjulio damartinezjulio commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

What

Adds the next SIG-107 runner batch in one PR:

  • opengrep default-on workflow input plus opengrep-config ruleset input.
  • brakeman default-on workflow input for detected Rails apps.
  • Static manifest + SARIF metadata contract rows for both tools.
  • Runner wrappers that keep scan.yml under the 1k-line cap.
  • Tests for workflow wiring, manifest rows, metadata IDs, Brakeman path normalization, and wrapper hardening.

Why

This makes two more high-signal CodeRabbit-style tools available to Sigilix evidence ingestion today: OpenGrep for broad SAST recall, and Brakeman for Rails-specific security findings.

Hardening

  • OpenGrep release binaries are version/SHA pinned for x86_64 and aarch64.
  • OpenGrep config values reject empty values, leading -, and unsupported characters.
  • Brakeman only installs after Rails roots are detected.
  • Brakeman bypasses caller config/ignore files and includes ignored warnings.
  • Brakeman/racc gems are fetched with --norc --clear-sources, SHA256 verified, then installed locally from verified .gem files.
  • Brakeman roots reject traversal segments and must resolve under SOURCE_DIR.
  • Nested Rails SARIF paths are normalized with root prefixes before merge.
  • Copy/merge/normalization failures are non-fatal and leave manifest-visible missing output rather than partial SARIF.

Verification

  • Red targeted tests first: missing inputs/scripts/manifest IDs failed as expected.
  • PYTHONPATH=.github/scripts python3 -m unittest security_sast_tools_workflow_test sigilix_sarif_test
  • Full runner test/compile gate:
    • python3 .github/scripts/sigilix_sarif_test.py
    • python3 .github/scripts/eslint_ts_workflow_test.py
    • python3 .github/scripts/tsc_workflow_test.py
    • python3 .github/scripts/pylint_workflow_test.py
    • python3 .github/scripts/trufflehog_converter_test.py
    • python3 .github/scripts/language_config_tools_workflow_test.py
    • python3 .github/scripts/knip_workflow_test.py
    • python3 .github/scripts/biome_workflow_test.py
    • python3 .github/scripts/docs_config_tools_test.py
    • python3 .github/scripts/oxlint_workflow_test.py
    • python3 .github/scripts/ast_grep_workflow_test.py
    • python3 .github/scripts/policy_iac_tools_workflow_test.py
    • python3 .github/scripts/security_sast_tools_workflow_test.py
    • python3 -m py_compile .github/scripts/*.py
  • bash -n .github/scripts/run_opengrep.sh .github/scripts/run_brakeman.sh
  • YAML parse via temporary PyYAML venv.
  • Pinned actionlint v1.7.12 macOS binary: clean.
  • git diff --check
  • File cap check: largest file is .github/workflows/scan.yml at 997 lines.

Reviewer sign-off

  • design-critic: CHANGE-FIRST initially; adjusted design to avoid Brakeman caller config inheritance and reduce default-on install cost via Rails detection.
  • security-auditor: SHIP after final hardening; no concrete exploitable blockers found.
  • code-reviewer: SHIP after final hardening; no true blockers found.

Summary by CodeRabbit

  • New Features

    • Integrated OpenGrep scanning with configurable security rulesets (default: p/security-audit,p/owasp-top-ten)
    • Integrated Brakeman scanning for Rails applications
    • Both tools enabled by default with SARIF output generation
  • Documentation

    • Updated workflow documentation and README with new tool configurations

@sigilix

sigilix Bot commented Jun 12, 2026

Copy link
Copy Markdown

Sigilix Overview

Effort: 4/5 (large)

Quality gates

  • ✅ PR title follows convention
  • ✅ PR description is complete
  • ℹ️ PR is linked to an issue — No Closes #N / Closes SIG-N keyword found in PR body or commit messages.

Summary — latest push

Adds OpenGrep and Brakeman as default-on SAST runner tools, with version/SHA-pinned binary and gem fetching, config validation, Rails root discovery with path-traversal protection, and SARIF path normalization for nested monorepo roots. Both tools are wired into scan.yml, registered in the manifest and SARIF contract, and covered by targeted tests that verify workflow wiring, manifest rows, metadata IDs, Brakeman path normalization, and wrapper hardening.

Important files

File Score Notes Next step
.github/scripts/run_brakeman.sh 5/5 Brakeman runner that discovers Rails roots, fetches SHA256-verified gems with --norc --clear-sources, bypasses caller config, normalizes SARIF paths for nested roots, and merges multi-root output — all with non-fatal failure handling. Verify the discover_rails_roots find command correctly handles deeply nested monorepo structures and that the while IFS= read -r -d '' root loop correctly consumes the null-delimited output from the sorted list.
.github/scripts/run_opengrep.sh 4/5 OpenGrep runner that downloads version/SHA-pinned release binaries, validates config rulesets against empty values and unsupported characters, and runs scans with SARIF output capped via the contract script. Add a unit test asserting that parse_opengrep_configs correctly splits comma-separated values and rejects leading dashes or invalid characters to complement the existing integration checks.
.github/scripts/brakeman_sarif_paths.py 4/5 Normalizes Brakeman SARIF artifact URIs by stripping file:// schemes and prefixing nested Rails root paths so monorepo results resolve correctly. Add a test case for a URI that already contains the root prefix to ensure _prefixed_path avoids double-prefixing paths like services/billing/services/billing/app/models/user.rb.
.github/scripts/security_sast_tools_workflow_test.py 3/5 Tests that both tools are default-on in workflow inputs, properly manifested, delegate to runner scripts, and that Brakeman SARIF paths are correctly prefixed for nested roots. Extend the BrakemanSarifPathTest to cover edge cases like roots with . values and URIs containing file://localhost schemes to ensure _strip_file_uri handles them.
.github/workflows/scan.yml 3/5 Wires opengrep and brakeman as default-on boolean inputs, adds opengrep-config string input, and delegates execution to the new runner scripts. Confirm the opengrep-config input is properly quoted when passed as OPENGREP_CONFIG to prevent shell splitting of comma-separated rulesets.

Sequence diagram

sequenceDiagram
    participant WF as scan.yml
    participant OG as run_opengrep.sh
    participant BK as run_brakeman.sh
    participant NM as brakeman_sarif_paths.py
    participant MG as sigilix_sarif_merge.py
    participant CT as sigilix_sarif_contract.py
    WF->>OG: OPENGREP_ENABLED, OPENGREP_CONFIG
    OG->>OG: Download & verify binary
    OG->>OG: Scan source
    OG->>CT: Normalize opengrep.sarif
    WF->>BK: BRAKEMAN_ENABLED
    BK->>BK: discover_rails_roots
    BK->>BK: Fetch & verify gems
    loop For each Rails root
        BK->>BK: Run Brakeman scan
        BK->>NM: Normalize paths with root prefix
    end
    alt Multiple roots
        BK->>MG: Merge normalized SARIF files
    end
    BK->>CT: Normalize brakeman.sarif
Loading

Confidence: 4/5

Both runners use SHA256-verified downloads, non-fatal error handling, and path-traversal protection with targeted tests, though the Brakeman multi-root merge and OpenGrep config parsing edge cases warrant careful review.

  • Verify discover_rails_roots in run_brakeman.sh correctly handles null-delimited output from sort -zu in the while IFS= read -r -d '' root loop (lines 68-77, 119-154).
  • Check _prefixed_path in brakeman_sarif_paths.py for double-prefixing when a URI already starts with the root string (lines 28-33).
  • Confirm parse_opengrep_configs in run_opengrep.sh correctly handles whitespace around commas and rejects all invalid characters (lines 27-46).
  • Ensure the opengrep-config workflow input is properly quoted when exported as OPENGREP_CONFIG in scan.yml to prevent shell word-splitting.
  • Validate that Brakeman's --config-file and --ignore-config flags correctly override any caller-provided config files in all gem versions.

Suggested labels: security

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR integrates OpenGrep and Brakeman security SAST tools into the scan workflow. It adds tool manifest entries, implements runner scripts with pinned versions and SHA256 verification, configures SARIF normalization and output merging, and extends test coverage to validate workflow wiring and script integrity.

Changes

OpenGrep and Brakeman SAST Integration

Layer / File(s) Summary
Tool registry and contract constants
.github/config/tool-manifest.json, .github/scripts/sigilix_sarif_contract.py, .github/scripts/sigilix_sarif_test.py
Tool manifest entries wire opengrep and brakeman to their enabled environment variables and SARIF outputs. SARIF contract constants recognize these tool IDs with display names. Test constants define expected output filenames and validate tool coverage in workflows.
OpenGrep runner script and workflow integration
.github/scripts/run_opengrep.sh, .github/workflows/scan.yml
Runner script parses comma-separated rulesets into --config arguments, downloads and verifies the binary by CPU architecture, runs opengrep scan with directory exclusions, and normalizes output via SARIF contract. Scan workflow inputs enable/configure OpenGrep (default p/security-audit,p/owasp-top-ten); environment pins version and platform checksums; conditional step invokes the runner with config from inputs.
Brakeman SARIF utility and runner script
.github/scripts/brakeman_sarif_paths.py, .github/scripts/run_brakeman.sh, .github/workflows/scan.yml
Utility normalizes SARIF artifact URIs for nested Rails roots by stripping file:// schemes, prefixing paths, and updating driver names. Runner discovers Rails roots by locating config/application.rb while pruning build/vendor directories, runs Brakeman per root, normalizes and merges per-root SARIF outputs, and applies final SARIF contract processing. Scan workflow inputs enable Brakeman; environment pins gem version and SHA256; conditional step executes the runner with isolated gem environment.
Workflow integration, test coverage, and documentation
.github/workflows/ci.yml, .github/workflows/scan.yml, .github/scripts/security_sast_tools_workflow_test.py, .github/scripts/sigilix_sarif_test.py, README.md
CI workflow runs new security SAST workflow test module. Scan workflow manifest generation includes OPENGREP_ENABLED and BRAKEMAN_ENABLED flags. Comprehensive test coverage validates that tools are default-enabled, manifest matches expected IDs and outputs, workflow steps delegate to correct runners, scripts contain version/checksum pinning and expected config/merge behaviors, and SARIF path normalization handles nested roots. README documents both tools in the catalog, default status, input flags, and OpenGrep ruleset configuration format.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Sigilix/runner#1: Both PRs modify the shared reusable .github/workflows/scan.yml workflow—the retrieved PR introduces the Semgrep→OIDC receipt pipeline, and the main PR extends that same scan workflow by adding OpenGrep/Brakeman inputs, SARIF steps, and env/manifest wiring.
  • Sigilix/runner#23: Both PRs extend the same Sigilix SARIF contract wiring by updating sigilix_sarif_contract.py's KNOWN_TOOL_IDS/DEFAULT_TOOL_NAMES (main adds opengrep/brakeman, retrieved adds ast-grep) and corresponding scan/workflow test expectations for the new tool.

Suggested labels

enhancement

Suggested reviewers

  • sigilix

Poem

🐰 Two guardians join the scan,
OpenGrep and Brakeman's plan,
With pinned versions, locked down tight,
And SARIF paths put right,
Security checks now shine so bright! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding two new SAST runner tools (OpenGrep and Brakeman) to support SIG-107 security scanning requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sig-107

Comment @coderabbitai help to get the list of available commands and usage tips.

@sigilix sigilix Bot added the enhancement New feature or request label Jun 12, 2026
@damartinezjulio

Copy link
Copy Markdown
Contributor Author

Responding to the Sigilix overview notes:

  • discover_rails_roots uses null-delimited output end to end: find -print0 | sort -zu, then the scan loop consumes it with while IFS= read -r -d '' root. I also verified locally that root-level ./config/application.rb matches the current pattern.
  • _prefixed_path already avoids double-prefixing with normalized == root or normalized.startswith(f"{root}/"); the targeted test covers nested-root prefixing plus absolute/file URI normalization.
  • parse_opengrep_configs trims comma-separated values, rejects empty values, rejects leading -, and now rejects : and other unsupported characters through the allowlist.
  • opengrep-config is passed through the workflow env: block as OPENGREP_CONFIG; local actionlint v1.7.12 passed, so the expression/env wiring is valid and not shell-split before the wrapper parses commas.
  • Brakeman config override was checked against Brakeman's CLI options: the wrapper passes runner-owned --config-file and --ignore-config files, plus --show-ignored, so caller config/ignore files do not suppress findings.

Validation for this commit: full runner Python test/compile gate, bash -n for the new wrappers, YAML parse, local pinned actionlint, git diff --check, and reviewer SHIP from code-reviewer + security-auditor.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/scripts/run_opengrep.sh:
- Around line 31-56: The parser in parse_opengrep_configs uses IFS=',' read -r
-a config_items which drops a trailing empty field (so "p/security-audit,"
bypasses the empty-rule check); fix by making the split preserve trailing
empties (for example, change the read to operate on "${OPENGREP_CONFIG}," e.g.
IFS=',' read -r -a config_items <<< "${OPENGREP_CONFIG}," so a terminal comma
produces an empty element to be caught by the existing trimmed empty check),
keep using the same trimming and validation logic on each item (symbols:
parse_opengrep_configs, config_items, trimmed, config_args).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70f847b3-84c6-4030-a9ef-86205221ff6b

📥 Commits

Reviewing files that changed from the base of the PR and between 365464f and a73fc07.

📒 Files selected for processing (10)
  • .github/config/tool-manifest.json
  • .github/scripts/brakeman_sarif_paths.py
  • .github/scripts/run_brakeman.sh
  • .github/scripts/run_opengrep.sh
  • .github/scripts/security_sast_tools_workflow_test.py
  • .github/scripts/sigilix_sarif_contract.py
  • .github/scripts/sigilix_sarif_test.py
  • .github/workflows/ci.yml
  • .github/workflows/scan.yml
  • README.md

Comment on lines +31 to +56
parse_opengrep_configs() {
local item
local trimmed
IFS=',' read -r -a config_items <<< "$OPENGREP_CONFIG"
for item in "${config_items[@]}"; do
trimmed="${item#"${item%%[![:space:]]*}"}"
trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
if [ -z "$trimmed" ]; then
echo "::warning::OpenGrep config contains an empty ruleset - manifest will record missing output."
return 1
fi
if [[ "$trimmed" == -* ]]; then
echo "::warning::OpenGrep config '$trimmed' must not start with '-' - manifest will record missing output."
return 1
fi
if [[ ! "$trimmed" =~ ^[A-Za-z0-9._/@-]+$ ]]; then
echo "::warning::OpenGrep config '$trimmed' contains unsupported characters - manifest will record missing output."
return 1
fi
config_args+=(--config "$trimmed")
done
if [ "${#config_args[@]}" -eq 0 ]; then
echo "::warning::OpenGrep config is empty - manifest will record missing output."
return 1
fi
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

OPENGREP_CONFIG='p/security-audit,'
IFS=',' read -r -a config_items <<< "$OPENGREP_CONFIG"

printf 'items=%s\n' "${`#config_items`[@]}"
for item in "${config_items[@]}"; do
  printf '<%s>\n' "$item"
done

Repository: Sigilix/runner

Length of output: 85


Reject trailing empty OpenGrep rulesets too

IFS=',' read -r -a config_items <<< "$OPENGREP_CONFIG" drops a terminal empty field: with OPENGREP_CONFIG='p/security-audit,' the parser sees only one element (<p/security-audit>) and never hits the -z "$trimmed" rejection. This bypasses the “empty ruleset” validation contract for trailing-comma input.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/scripts/run_opengrep.sh around lines 31 - 56, The parser in
parse_opengrep_configs uses IFS=',' read -r -a config_items which drops a
trailing empty field (so "p/security-audit," bypasses the empty-rule check); fix
by making the split preserve trailing empties (for example, change the read to
operate on "${OPENGREP_CONFIG}," e.g. IFS=',' read -r -a config_items <<<
"${OPENGREP_CONFIG}," so a terminal comma produces an empty element to be caught
by the existing trimmed empty check), keep using the same trimming and
validation logic on each item (symbols: parse_opengrep_configs, config_items,
trimmed, config_args).

@damartinezjulio
damartinezjulio merged commit 5b9c52b into main Jun 12, 2026
6 checks passed
@damartinezjulio
damartinezjulio deleted the sig-107 branch June 12, 2026 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant