27a469b169
The trivy install script otherwise queries api.github.com unauthenticated for the latest release tag. Self-hosted runners hit rate-limit quickly when multiple workflows run in succession, leading to empty version resolution and install failures. Version pinned with renovate annotation so updates flow through Renovate.
86 lines
3.5 KiB
YAML
86 lines
3.5 KiB
YAML
name: Security Scan (reusable)
|
|
|
|
# Reusable workflow consumed by per-repo security.yml stubs across the
|
|
# Hellion stack. Runs Semgrep SAST + Trivy filesystem scan sequentially
|
|
# inside a single job. Either tool failing fails the calling workflow.
|
|
#
|
|
# Why one job, not two parallel jobs:
|
|
# act_runner v0.6.1 has a race condition when two jobs in the same task
|
|
# share a workspace and chown it in parallel — one container never gets
|
|
# /var/run/act/ provisioned and silent-fails. Sequential steps avoid it.
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
severity:
|
|
description: 'Trivy severity threshold (e.g. CRITICAL,HIGH or just CRITICAL)'
|
|
required: false
|
|
type: string
|
|
default: 'CRITICAL,HIGH'
|
|
semgrep-config:
|
|
description: 'Semgrep config (default auto detects rules per language)'
|
|
required: false
|
|
type: string
|
|
default: 'auto'
|
|
semgrep-exclude-rules:
|
|
description: 'Semgrep rule IDs to exclude, comma-separated (e.g. csharp.lang.security.sqli.csharp-sqli)'
|
|
required: false
|
|
type: string
|
|
default: ''
|
|
|
|
jobs:
|
|
scan:
|
|
name: Security Scan
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.x'
|
|
|
|
- name: Install Semgrep
|
|
run: pip install --no-cache-dir semgrep
|
|
|
|
- name: Install Trivy
|
|
# Direct install via the official upstream script. The aquasecurity/
|
|
# trivy-action wrapper does nested checkouts and auth-juggling that
|
|
# does not play well with Self-Hosted Gitea Actions, this is more
|
|
# robust and a smaller surface.
|
|
#
|
|
# Version pinned: the install script otherwise hits api.github.com to
|
|
# resolve "latest", which is unauthenticated and burns through the
|
|
# self-hosted runner's GitHub rate-limit on each push. Pinning skips
|
|
# the API call entirely. Renovate-bot keeps the version current:
|
|
# renovate: datasource=github-releases depName=aquasecurity/trivy
|
|
run: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin v0.70.0
|
|
|
|
- name: Run Semgrep SAST
|
|
# --config=auto pulls language-appropriate rule packs from semgrep.dev
|
|
# without requiring an account. --error makes the step fail when ERROR
|
|
# findings exist. WARNING-level rules still run for visibility but do
|
|
# not fail the build (they would dominate the noise).
|
|
# Per-repo rule exclusion via the semgrep-exclude-rules input.
|
|
env:
|
|
EXCLUDE_RULES: ${{ inputs.semgrep-exclude-rules }}
|
|
run: |
|
|
args="--config=${{ inputs.semgrep-config }} --error --severity=ERROR"
|
|
if [ -n "$EXCLUDE_RULES" ]; then
|
|
for rule in $(echo "$EXCLUDE_RULES" | tr ',' ' '); do
|
|
args="$args --exclude-rule=$rule"
|
|
done
|
|
fi
|
|
echo "Running: semgrep scan $args"
|
|
semgrep scan $args
|
|
|
|
- name: Run Trivy filesystem scan
|
|
# if: always() — surface Trivy findings even when Semgrep fails first,
|
|
# so a single run gives the full combined picture.
|
|
# Scans dependency manifests (NuGet, npm, package-lock etc.) against
|
|
# the NVD CVE database. --ignore-unfixed skips findings that have
|
|
# no patched version available so we focus on actionable items.
|
|
if: always()
|
|
run: trivy fs --severity ${{ inputs.severity }} --exit-code 1 --ignore-unfixed .
|