Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .github/config/tool-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"env": "SEMGREP_ENABLED",
"output": "semgrep.sarif"
},
{
"id": "opengrep",
"env": "OPENGREP_ENABLED",
"output": "opengrep.sarif"
},
{
"id": "eslint",
"env": "ESLINT_ENABLED",
Expand All @@ -20,6 +25,11 @@
"env": "RUFF_ENABLED",
"output": "ruff.sarif"
},
{
"id": "brakeman",
"env": "BRAKEMAN_ENABLED",
"output": "brakeman.sarif"
},
{
"id": "pylint",
"env": "PYLINT_ENABLED",
Expand Down
81 changes: 81 additions & 0 deletions .github/scripts/brakeman_sarif_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import argparse
import json
import sys
from urllib.parse import urlparse

from sarif_converter_common import load_json_file, normalize_path, write_json_file


def _is_external_uri(uri):
parsed = urlparse(uri)
return bool(parsed.scheme and parsed.scheme.lower() != "file")


def _strip_file_uri(uri):
parsed = urlparse(uri)
if parsed.scheme.lower() != "file":
return uri
return parsed.path or ""


def _prefixed_path(uri, root, base_dir):
if not isinstance(uri, str) or not uri or _is_external_uri(uri):
return uri
normalized = normalize_path(_strip_file_uri(uri), base_dir=base_dir)
if root == "." or normalized == root or normalized.startswith(f"{root}/"):
return normalized
return f"{root}/{normalized}"


def _normalize_artifact_locations(value, root, base_dir):
if isinstance(value, list):
for item in value:
_normalize_artifact_locations(item, root, base_dir)
return
if not isinstance(value, dict):
return
if isinstance(value.get("uri"), str):
value["uri"] = _prefixed_path(value["uri"], root, base_dir)
for child in value.values():
_normalize_artifact_locations(child, root, base_dir)


def normalize_brakeman_sarif_paths(document, root, base_dir="."):
if not isinstance(document, dict):
raise ValueError("invalid Brakeman SARIF input")
if document.get("version") != "2.1.0":
document["version"] = "2.1.0"
runs = document.get("runs")
if not isinstance(runs, list):
document["runs"] = []
return document
root = normalize_path(root or ".", base_dir=base_dir)
for run in runs:
if not isinstance(run, dict):
continue
driver = run.setdefault("tool", {}).setdefault("driver", {})
if root != ".":
driver["name"] = f"Brakeman ({root})"
_normalize_artifact_locations(run.get("results"), root, base_dir)
return document


def _main(argv):
parser = argparse.ArgumentParser(description="Normalize Brakeman SARIF paths for nested Rails roots.")
parser.add_argument("input")
parser.add_argument("output")
parser.add_argument("--root", required=True)
parser.add_argument("--base-dir", default=".")
args = parser.parse_args(argv)

document = load_json_file(args.input)
if not isinstance(document, dict):
print("invalid Brakeman SARIF input", file=sys.stderr)
return 1
normalized = normalize_brakeman_sarif_paths(document, root=args.root, base_dir=args.base_dir)
write_json_file(args.output, normalized)
return 0


if __name__ == "__main__":
sys.exit(_main(sys.argv[1:]))
175 changes: 175 additions & 0 deletions .github/scripts/run_brakeman.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env bash
set -euo pipefail

: "${BRAKEMAN_GEM_SHA256:?}"
: "${BRAKEMAN_VERSION:?}"
: "${RACC_GEM_SHA256:?}"
: "${RACC_VERSION:?}"
: "${RESULT_CAP:?}"
: "${RUNNER_DIR:?}"
: "${RUNNER_TEMP:?}"
: "${SARIF_DIR:?}"
: "${SOURCE_DIR:?}"

SOURCE_DIR="$(cd "$SOURCE_DIR" && pwd -P)"
RUNNER_DIR="$(cd "$RUNNER_DIR" && pwd -P)"

raw="$SARIF_DIR/brakeman.raw.sarif"
out="$SARIF_DIR/brakeman.sarif"
roots_list=""
brakeman_config="$RUNNER_TEMP/brakeman-sigilix.yml"
brakeman_ignore="$RUNNER_TEMP/brakeman-ignore.json"
brakeman_gem_cache="$RUNNER_TEMP/brakeman-gem-cache"
brakeman_raw_dir="$RUNNER_TEMP/brakeman-raw"
raw_files=()

mkdir -p "$SARIF_DIR" "$RUNNER_TEMP" "$brakeman_gem_cache" "$brakeman_raw_dir"
roots_list="$(mktemp "$RUNNER_TEMP/brakeman-roots.XXXXXX")"

cleanup_brakeman() {
rm -f "$roots_list" "$brakeman_config" "$brakeman_ignore"
rm -rf "$brakeman_gem_cache" "$brakeman_raw_dir"
}
trap cleanup_brakeman EXIT

emit_empty_sarif() {
printf '{"version":"2.1.0","runs":[]}' > "$raw"
}

discover_rails_roots() {
find -P . \
\( -type d \( -name '.git' -o -name 'node_modules' -o -name 'dist' -o -name 'build' \
-o -name 'coverage' -o -name '.next' -o -name 'out' -o -name 'vendor' -o -name '.terraform' \) -prune \) -o \
\( -type f -path '*/config/application.rb' -print0 \) \
| while IFS= read -r -d '' application_file; do
root="${application_file%/config/application.rb}"
printf '%s\0' "${root:-.}"
done \
| sort -zu
}

write_runner_brakeman_config() {
printf '%s\n' '--- {}' > "$brakeman_config"
printf '%s\n' '{"ignored_warnings":[]}' > "$brakeman_ignore"
}

fetch_verified_gem() {
local name="$1"
local version="$2"
local checksum="$3"
local path="$brakeman_gem_cache/${name}-${version}.gem"
rm -f "$path"
if ! (cd "$brakeman_gem_cache" && gem fetch --norc --clear-sources --source https://rubygems.org "$name" -v "$version" >/dev/null); then
echo "::warning::${name} gem fetch failed - manifest will record missing output."
rm -f "$path"
return 1
fi
if [ ! -s "$path" ]; then
echo "::warning::${name} gem package missing after fetch - manifest will record missing output."
return 1
fi
if ! printf '%s %s\n' "$checksum" "$path" | sha256sum -c --strict -; then
echo "::warning::${name} gem checksum mismatch - manifest will record missing output."
return 1
fi
}

cd "$SOURCE_DIR"
if ! discover_rails_roots > "$roots_list"; then
echo "::warning::Brakeman Rails root discovery failed - emitting empty Brakeman SARIF run."
emit_empty_sarif
elif ! grep -qz . "$roots_list"; then
echo "::notice::No Rails roots found - emitting empty Brakeman SARIF run."
emit_empty_sarif
elif [[ ! "$BRAKEMAN_VERSION" =~ ^[0-9]+[.][0-9]+[.][0-9]+$ ]]; then
echo "::warning::Brakeman version must be a pinned x.y.z version - manifest will record missing output."
elif [[ ! "$RACC_VERSION" =~ ^[0-9]+[.][0-9]+[.][0-9]+$ ]]; then
echo "::warning::racc version must be a pinned x.y.z version - manifest will record missing output."
elif [[ ! "$BRAKEMAN_GEM_SHA256" =~ ^[0-9a-f]{64}$ ]] || [[ ! "$RACC_GEM_SHA256" =~ ^[0-9a-f]{64}$ ]]; then
echo "::warning::Brakeman gem checksums must be pinned SHA256 values - manifest will record missing output."
elif ! command -v ruby >/dev/null 2>&1 || ! command -v gem >/dev/null 2>&1; then
echo "::warning::Ruby and gem are required for Brakeman - manifest will record missing output."
else
export GEM_HOME="$RUNNER_TEMP/brakeman-gems"
export GEM_PATH="$GEM_HOME"
export PATH="$GEM_HOME/bin:$PATH"
mkdir -p "$GEM_HOME"
write_runner_brakeman_config
if ! fetch_verified_gem racc "$RACC_VERSION" "$RACC_GEM_SHA256"; then
:
elif ! fetch_verified_gem brakeman "$BRAKEMAN_VERSION" "$BRAKEMAN_GEM_SHA256"; then
:
elif ! gem install --norc --local --no-document --install-dir "$GEM_HOME" \
"$brakeman_gem_cache/racc-${RACC_VERSION}.gem" \
"$brakeman_gem_cache/brakeman-${BRAKEMAN_VERSION}.gem"; then
echo "::warning::Brakeman install failed - manifest will record missing output."
elif ! brakeman_version="$(brakeman --version 2>/dev/null)"; then
echo "::warning::Brakeman version check failed - manifest will record missing output."
elif ! printf '%s\n' "$brakeman_version" | grep -Eq "(^|[^0-9.])${BRAKEMAN_VERSION}([^0-9.]|$)"; then
echo "::warning::Brakeman installed version mismatch - manifest will record missing output."
else
index=0
while IFS= read -r -d '' root; do
root="${root#./}"
if [ -z "$root" ]; then root="."; fi
if [[ "$root" == ".." || "$root" == "../"* || "$root" == *"/.." || "$root" == *"/../"* ]]; then
echo "::warning::Skipping Brakeman root with traversal segments: ${root}"
continue
fi
if [ "$root" = "." ]; then
root_abs="$SOURCE_DIR"
elif ! root_abs="$(cd "$SOURCE_DIR/$root" && pwd -P)"; then
echo "::warning::Unable to resolve Brakeman root ${root} - skipping."
continue
fi
if [[ "$root_abs" != "$SOURCE_DIR" && "$root_abs" != "$SOURCE_DIR"/* ]]; then
echo "::warning::Brakeman root ${root} resolves outside source directory - skipping."
continue
fi
root_raw="$brakeman_raw_dir/brakeman-${index}.raw.sarif"
root_normalized="$brakeman_raw_dir/brakeman-${index}.sarif"
if brakeman \
--path "$root_abs" \
--config-file "$brakeman_config" \
--ignore-config "$brakeman_ignore" \
--show-ignored \
--no-exit-on-warn \
--no-exit-on-error \
--format sarif \
--output "$root_raw" \
--quiet; then
:
else
echo "::warning::Brakeman scan for ${root} exited non-zero - using SARIF output if present."
fi
if [ -s "$root_raw" ]; then
if python3 "$RUNNER_DIR/.github/scripts/brakeman_sarif_paths.py" \
"$root_raw" "$root_normalized" --root "$root" --base-dir "$SOURCE_DIR" \
&& [ -s "$root_normalized" ]; then
raw_files+=("$root_normalized")
else
echo "::warning::Brakeman SARIF path normalization failed for ${root} - discarding output."
fi
else
echo "::warning::Brakeman scan for ${root} produced no SARIF output."
fi
index=$((index + 1))
done < "$roots_list"
if [ "${#raw_files[@]}" -eq 0 ]; then
echo "::warning::Brakeman produced no SARIF output for detected Rails roots - manifest will record missing output."
elif [ "${#raw_files[@]}" -eq 1 ]; then
cp "${raw_files[0]}" "$raw" \
|| { echo "::warning::Brakeman failed to copy SARIF output - manifest will record missing output."; rm -f "$raw"; }
else
python3 "$RUNNER_DIR/.github/scripts/sigilix_sarif_merge.py" \
"${raw_files[@]}" -o "$raw" \
|| { echo "::warning::Brakeman SARIF merge failed - manifest will record missing output."; rm -f "$raw"; }
fi
fi
fi

if [ -s "$raw" ]; then
python3 "$RUNNER_DIR/.github/scripts/sigilix_sarif_contract.py" \
brakeman "$raw" "$out" --cap "$RESULT_CAP" --ensure-run \
|| echo "::warning::Brakeman SARIF normalization failed - manifest will record missing output."
fi
116 changes: 116 additions & 0 deletions .github/scripts/run_opengrep.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/usr/bin/env bash
set -euo pipefail

: "${OPENGREP_MANYLINUX_AARCH64_SHA256:?}"
: "${OPENGREP_MANYLINUX_X86_SHA256:?}"
: "${OPENGREP_VERSION:?}"
: "${RESULT_CAP:?}"
: "${RUNNER_DIR:?}"
: "${RUNNER_TEMP:?}"
: "${SARIF_DIR:?}"
: "${SOURCE_DIR:?}"
: "${OPENGREP_CONFIG:=p/security-audit,p/owasp-top-ten}"

SOURCE_DIR="$(cd "$SOURCE_DIR" && pwd -P)"
RUNNER_DIR="$(cd "$RUNNER_DIR" && pwd -P)"

raw="$SARIF_DIR/opengrep.raw.sarif"
out="$SARIF_DIR/opengrep.sarif"
opengrep_bin="$RUNNER_TEMP/opengrep"
asset=""
checksum=""
config_args=()

mkdir -p "$SARIF_DIR" "$RUNNER_TEMP"

cleanup_opengrep() {
rm -f "$opengrep_bin"
}
trap cleanup_opengrep EXIT

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
}
Comment on lines +31 to +56

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).


case "$(uname -m)" in
x86_64|amd64)
asset="opengrep_manylinux_x86"
checksum="$OPENGREP_MANYLINUX_X86_SHA256"
;;
aarch64|arm64)
asset="opengrep_manylinux_aarch64"
checksum="$OPENGREP_MANYLINUX_AARCH64_SHA256"
;;
*)
echo "::warning::OpenGrep unsupported runner architecture $(uname -m) - manifest will record missing output."
;;
esac

cd "$SOURCE_DIR"
if [ -z "$asset" ]; then
:
elif [[ ! "$OPENGREP_VERSION" =~ ^[0-9]+[.][0-9]+[.][0-9]+$ ]]; then
echo "::warning::OpenGrep version must be a pinned x.y.z version - manifest will record missing output."
elif [[ ! "$checksum" =~ ^[0-9a-f]{64}$ ]]; then
echo "::warning::OpenGrep checksum must be a pinned SHA256 value - manifest will record missing output."
elif ! parse_opengrep_configs; then
:
elif ! curl -fsSL -o "$opengrep_bin" \
"https://github.com/opengrep/opengrep/releases/download/v${OPENGREP_VERSION}/${asset}"; then
echo "::warning::OpenGrep download failed - manifest will record missing output."
elif ! printf '%s %s\n' "$checksum" "$opengrep_bin" | sha256sum -c --strict -; then
echo "::warning::OpenGrep checksum mismatch - manifest will record missing output."
elif ! chmod +x "$opengrep_bin"; then
echo "::warning::OpenGrep chmod failed - manifest will record missing output."
elif ! opengrep_version="$("$opengrep_bin" --version 2>/dev/null)"; then
echo "::warning::OpenGrep version check failed - manifest will record missing output."
elif ! printf '%s\n' "$opengrep_version" | grep -Eq "(^|[^0-9.])${OPENGREP_VERSION}([^0-9.]|$)"; then
echo "::warning::OpenGrep installed version mismatch - manifest will record missing output."
else
"$opengrep_bin" scan \
"${config_args[@]}" \
--exclude=node_modules \
--exclude=dist \
--exclude=build \
--exclude=coverage \
--exclude=.next \
--exclude=out \
--exclude=.venv \
--exclude=vendor \
--exclude=.tox \
--exclude=.terraform \
--sarif-output="$raw" \
. || true
if [ ! -s "$raw" ]; then
echo "::warning::OpenGrep scan produced no SARIF output - manifest will record missing output."
fi
fi

if [ -s "$raw" ]; then
python3 "$RUNNER_DIR/.github/scripts/sigilix_sarif_contract.py" \
opengrep "$raw" "$out" --cap "$RESULT_CAP" --ensure-run \
|| echo "::warning::OpenGrep SARIF normalization failed - manifest will record missing output."
fi
Loading
Loading