GitRacks 28.1.0+gitracks.1

This commit is contained in:
gitracks committed 2026-01-01 00:00:00 +00:00
commit f73f768c5b
2 files changed
+255

No files matched your search

+240
View File
@@ -0,0 +1,240 @@
# GitRacks security scanning: Trivy (dependencies, licenses, SBOM), Gitleaks (secrets) and Semgrep (SAST).
# Each scanner's SARIF goes to GitRacks, which tracks the findings and sets the gitracks/security status (docs/fork/security.md).
#
# Release pins for runners without the tools on PATH: bump the versions with their sha256 from each release's checksums file.
on:
workflow_call:
inputs:
runs-on:
description: Runner label
type: string
default: ubuntu-latest
scanners:
description: Comma-separated subset of trivy,gitleaks,semgrep
type: string
default: trivy,gitleaks,semgrep
fail-on:
description: Fail the gitracks/security status while findings this severe or worse are open (critical, high, medium, low, note, none)
type: string
default: high
semgrep-config:
description: Semgrep --config; empty uses the bundled rules, p/default needs network
type: string
default: ""
sbom:
description: Upload a CycloneDX SBOM from Trivy as the artifact sbom, in a separate job (needs actions/upload-artifact)
type: boolean
default: true
offline:
description: Use the Trivy DB already in TRIVY_CACHE_DIR instead of downloading it
type: boolean
default: false
env:
TRIVY_VERSION: 0.76.0
TRIVY_SHA256_X64: e24b1c51293c1b15e4ba8d45bbc7891ba053f90278ec2698f3562c993e145fe7
TRIVY_SHA256_ARM64: aab21e677a7b7071fc79f1711480840754c18a1b9d9cefa7ccd06af63a0e8f4c
GITLEAKS_VERSION: 8.30.1
GITLEAKS_SHA256_X64: 551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb
GITLEAKS_SHA256_ARM64: e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080
SEMGREP_VERSION: 1.180.0
SEMGREP_ENABLE_VERSION_CHECK: "0"
jobs:
scan:
runs-on: ${{ inputs.runs-on }}
env:
TOKEN: ${{ gitea.token }}
SCANNERS: ",${{ inputs.scanners }},"
steps:
- name: Check out
run: |
git init -q .
auth=$(printf 'x-access-token:%s' "$TOKEN" | base64 | tr -d '\n')
git -c http.extraHeader="Authorization: Basic $auth" fetch -q --depth=1 "${{ gitea.server_url }}/${{ gitea.repository }}.git" "${{ gitea.sha }}"
git checkout -q FETCH_HEAD
out="${RUNNER_TEMP:-/tmp}/gitracks-security"
mkdir -p "$out/bin"
echo "OUT=$out" >> "$GITHUB_ENV"
echo "$out/bin" >> "$GITHUB_PATH"
- name: Install missing scanners
run: |
case "$(uname -m)" in
x86_64) trivy_arch=Linux-64bit gitleaks_arch=linux_x64 trivy_sum=$TRIVY_SHA256_X64 gitleaks_sum=$GITLEAKS_SHA256_X64 ;;
aarch64 | arm64) trivy_arch=Linux-ARM64 gitleaks_arch=linux_arm64 trivy_sum=$TRIVY_SHA256_ARM64 gitleaks_sum=$GITLEAKS_SHA256_ARM64 ;;
esac
fetch() { # url sha256 binary
curl -sSfL "$1" -o "$OUT/download.tar.gz"
echo "$2 $OUT/download.tar.gz" | sha256sum -c - > /dev/null
tar -xzf "$OUT/download.tar.gz" -C "$OUT/bin" "$3"
rm "$OUT/download.tar.gz"
}
if [[ $SCANNERS == *,trivy,* ]] && ! command -v trivy > /dev/null; then
fetch "https://github.com/aquasecurity/trivy/releases/download/v$TRIVY_VERSION/trivy_${TRIVY_VERSION}_$trivy_arch.tar.gz" "$trivy_sum" trivy
fi
if [[ $SCANNERS == *,gitleaks,* ]] && ! command -v gitleaks > /dev/null; then
fetch "https://github.com/gitleaks/gitleaks/releases/download/v$GITLEAKS_VERSION/gitleaks_${GITLEAKS_VERSION}_$gitleaks_arch.tar.gz" "$gitleaks_sum" gitleaks
fi
if [[ $SCANNERS == *,semgrep,* ]] && ! command -v semgrep > /dev/null; then
python3 -m venv "$OUT/semgrep"
"$OUT/semgrep/bin/pip" install -q "semgrep==$SEMGREP_VERSION"
ln -s "$OUT/semgrep/bin/semgrep" "$OUT/bin/semgrep"
fi
- name: Trivy
if: always() && contains(env.SCANNERS, ',trivy,')
run: |
flags=()
if [ "${{ inputs.offline }}" = true ]; then flags=(--skip-db-update --skip-java-db-update); fi
# Trivy reads Go licenses from $GOPATH/pkg/mod; an empty one keeps results independent of other jobs on the host
GOPATH="$OUT/gopath" trivy fs --quiet "${flags[@]}" --scanners vuln,license --format sarif -o "$OUT/trivy.sarif" .
- name: Gitleaks
if: always() && contains(env.SCANNERS, ',gitleaks,')
run: gitleaks dir . --no-banner --redact --report-format sarif --report-path "$OUT/gitleaks.sarif" --exit-code 0
- name: Semgrep
if: always() && contains(env.SCANNERS, ',semgrep,')
env:
CONFIG: ${{ inputs.semgrep-config }}
run: |
config=$CONFIG
if [ -z "$config" ]; then
# in the working directory, so rule IDs carry no runner-specific path prefix
config=.gitracks-semgrep.yml
cat > "$config" << 'RULES'
rules:
- id: gitracks.python.eval
languages: [python]
severity: ERROR
message: eval() and exec() run arbitrary code; parse the input instead, e.g. with ast.literal_eval.
pattern-either:
- pattern: eval($X)
- pattern: exec($X)
metadata: {category: security, cwe: "CWE-95"}
- id: gitracks.python.deserialization
languages: [python]
severity: ERROR
message: Deserializing untrusted data with this module can run code; use JSON for untrusted input.
pattern-either:
- pattern: pickle.loads(...)
- pattern: pickle.load(...)
- pattern: marshal.loads(...)
metadata: {category: security, cwe: "CWE-502"}
- id: gitracks.python.yaml-load
languages: [python]
severity: ERROR
message: yaml.load without SafeLoader can build arbitrary objects; use yaml.safe_load.
patterns:
- pattern: yaml.load(...)
- pattern-not: yaml.load(..., Loader=yaml.SafeLoader, ...)
- pattern-not: yaml.load(..., Loader=SafeLoader, ...)
metadata: {category: security, cwe: "CWE-502"}
- id: gitracks.python.shell
languages: [python]
severity: WARNING
message: Shell commands built from variables allow command injection; pass an argument list without shell=True.
pattern-either:
- pattern: subprocess.$F(..., shell=True, ...)
- pattern: os.system($X)
metadata: {category: security, cwe: "CWE-78"}
- id: gitracks.python.sql-format
languages: [python]
severity: WARNING
message: SQL built with string formatting allows SQL injection; use query parameters.
pattern-either:
- pattern: $C.execute(f"...")
- pattern: $C.execute("..." % ...)
- pattern: $C.execute("..." + ...)
- pattern: $C.execute("...".format(...))
metadata: {category: security, cwe: "CWE-89"}
- id: gitracks.js.eval
languages: [javascript, typescript]
severity: ERROR
message: Evaluating strings as code allows code injection.
pattern-either:
- pattern: eval($X)
- pattern: new Function(...)
metadata: {category: security, cwe: "CWE-95"}
- id: gitracks.js.child-process
languages: [javascript, typescript]
severity: WARNING
message: child_process.exec runs a shell; use execFile with an argument list.
pattern: require('child_process').exec(...)
metadata: {category: security, cwe: "CWE-78"}
- id: gitracks.go.tls-skip-verify
languages: [go]
severity: ERROR
message: InsecureSkipVerify disables TLS certificate checks.
pattern: "tls.Config{..., InsecureSkipVerify: true, ...}"
metadata: {category: security, cwe: "CWE-295"}
- id: gitracks.go.shell
languages: [go]
severity: WARNING
message: Running a command through a shell allows command injection; pass the arguments to exec.Command directly.
pattern-either:
- pattern: exec.Command("sh", "-c", ...)
- pattern: exec.Command("bash", "-c", ...)
metadata: {category: security, cwe: "CWE-78"}
- id: gitracks.go.sql-format
languages: [go]
severity: WARNING
message: SQL built with fmt.Sprintf allows SQL injection; use query parameters.
pattern-either:
- pattern: $DB.Query(fmt.Sprintf(...), ...)
- pattern: $DB.Exec(fmt.Sprintf(...), ...)
- pattern: $DB.QueryRow(fmt.Sprintf(...), ...)
metadata: {category: security, cwe: "CWE-89"}
RULES
fi
semgrep scan --quiet --metrics off --disable-version-check --config "$config" --sarif -o "$OUT/semgrep.sarif" .
- name: Upload findings
if: always()
env:
URL: ${{ gitea.api_url }}/repos/${{ gitea.repository }}/actions/reports?format=sarif&fail_on=${{ inputs.fail-on }}
run: |
status=0
for name in trivy gitleaks semgrep; do
[[ $SCANNERS == *,$name,* ]] || continue
if [ -s "$OUT/$name.sarif" ]; then
curl -sSf -o /dev/null -H "Authorization: token $TOKEN" -H "Content-Type: application/octet-stream" --data-binary "@$OUT/$name.sarif" "$URL&name=$name" || status=1
else
echo "::warning::$name wrote no report"
status=1
fi
done
exit $status
# a job of its own: runners fetch every action of a job before it starts, which would break offline scans
sbom:
if: inputs.sbom
runs-on: ${{ inputs.runs-on }}
env:
TOKEN: ${{ gitea.token }}
steps:
- name: Generate SBOM
run: |
git init -q .
auth=$(printf 'x-access-token:%s' "$TOKEN" | base64 | tr -d '\n')
git -c http.extraHeader="Authorization: Basic $auth" fetch -q --depth=1 "${{ gitea.server_url }}/${{ gitea.repository }}.git" "${{ gitea.sha }}"
git checkout -q FETCH_HEAD
out="${RUNNER_TEMP:-/tmp}/gitracks-sbom"
mkdir -p "$out"
if ! command -v trivy > /dev/null; then
case "$(uname -m)" in
x86_64) arch=Linux-64bit sum=$TRIVY_SHA256_X64 ;;
aarch64 | arm64) arch=Linux-ARM64 sum=$TRIVY_SHA256_ARM64 ;;
esac
curl -sSfL "https://github.com/aquasecurity/trivy/releases/download/v$TRIVY_VERSION/trivy_${TRIVY_VERSION}_$arch.tar.gz" -o "$out/trivy.tar.gz"
echo "$sum $out/trivy.tar.gz" | sha256sum -c - > /dev/null
tar -xzf "$out/trivy.tar.gz" -C "$out" trivy
export PATH="$out:$PATH"
fi
GOPATH="$out/gopath" trivy fs --quiet --format cyclonedx -o "$out/sbom.cdx.json" .
echo "SBOM=$out/sbom.cdx.json" >> "$GITHUB_ENV"
- uses: actions/upload-artifact@v3 # v4 refuses any server but github.com (GHESNotSupportedError)
with:
name: sbom
path: ${{ env.SBOM }}
+15
View File
@@ -0,0 +1,15 @@
# GitRacks security actions
A reusable workflow that runs Trivy, Gitleaks and Semgrep and uploads their SARIF reports to GitRacks:
```yaml
jobs:
security:
uses: gitracks/security-actions/.gitea/workflows/security.yml@v1
with:
fail-on: high
```
The NixOS module publishes this directory as the public repository `gitracks/security-actions` and moves its `v1` tag on upgrades (`services.gitracks.security.enable`). Elsewhere, push this directory to a public repository on your instance and tag it `v1`. Inputs and the severity gate are described in the GitRacks docs (`docs/fork/security.md`).
The bundled Semgrep rules are GitRacks' own (MIT).