297 lines
11 KiB
YAML
297 lines
11 KiB
YAML
name: CI/CD with Security Hardening
|
|
|
|
# Hard rule (branch + cadence policy):
|
|
# 1) Contributors branch from `dev` and open PRs into `dev`.
|
|
# 2) PRs into `main` are promotion PRs from `dev` (or explicit hotfix override).
|
|
# 3) Full CI/CD runs on merge/direct push to `main` and manual dispatch only.
|
|
# 3a) Main/manual build triggers are restricted to maintainers:
|
|
# `theonlyhennygod`, `jordanthejet`.
|
|
# 4) release published: run publish path on every release.
|
|
# Cost policy: no daily auto-release and no heavy PR-triggered release pipeline.
|
|
on:
|
|
workflow_dispatch:
|
|
release:
|
|
types: [published]
|
|
|
|
concurrency:
|
|
group: ci-cd-security-${{ github.event.pull_request.number || github.ref || github.run_id }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
GIT_CONFIG_COUNT: "1"
|
|
GIT_CONFIG_KEY_0: core.hooksPath
|
|
GIT_CONFIG_VALUE_0: /dev/null
|
|
CARGO_TERM_COLOR: always
|
|
|
|
jobs:
|
|
authorize-main-build:
|
|
name: Access and Execution Gate
|
|
runs-on: [self-hosted, Linux, X64, light, cpu40]
|
|
outputs:
|
|
run_pipeline: ${{ steps.gate.outputs.run_pipeline }}
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
with:
|
|
fetch-depth: 1
|
|
|
|
- name: Enforce actor policy and skip rules
|
|
id: gate
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
actor="${GITHUB_ACTOR}"
|
|
actor_lc="$(echo "${actor}" | tr '[:upper:]' '[:lower:]')"
|
|
event="${GITHUB_EVENT_NAME}"
|
|
allowed_humans_lc="theonlyhennygod,jordanthejet"
|
|
allowed_bot="github-actions[bot]"
|
|
run_pipeline="true"
|
|
|
|
if [[ "${event}" == "push" ]]; then
|
|
commit_msg="$(git log -1 --pretty=%B | tr -d '\r')"
|
|
if [[ "${commit_msg}" == *"[skip ci]"* ]]; then
|
|
run_pipeline="false"
|
|
echo "Skipping heavy pipeline because commit message includes [skip ci]."
|
|
fi
|
|
|
|
if [[ "${run_pipeline}" == "true" && ",${allowed_humans_lc}," != *",${actor_lc},"* ]]; then
|
|
echo "::error::Only maintainer actors (${allowed_humans_lc}) can trigger main build runs. Actor: ${actor}"
|
|
exit 1
|
|
fi
|
|
elif [[ "${event}" == "workflow_dispatch" ]]; then
|
|
if [[ ",${allowed_humans_lc}," != *",${actor_lc},"* ]]; then
|
|
echo "::error::Only maintainer actors (${allowed_humans_lc}) can run manual CI/CD dispatches. Actor: ${actor}"
|
|
exit 1
|
|
fi
|
|
elif [[ "${event}" == "release" ]]; then
|
|
if [[ ",${allowed_humans_lc}," != *",${actor_lc},"* && "${actor}" != "${allowed_bot}" ]]; then
|
|
echo "::error::Only maintainer actors (${allowed_humans_lc}) or ${allowed_bot} can trigger release build lanes. Actor: ${actor}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
echo "run_pipeline=${run_pipeline}" >> "$GITHUB_OUTPUT"
|
|
|
|
build-and-test:
|
|
needs: authorize-main-build
|
|
if: needs.authorize-main-build.outputs.run_pipeline == 'true'
|
|
runs-on: [self-hosted, Linux, X64, blacksmith-2vcpu-ubuntu-2404]
|
|
timeout-minutes: 90
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
|
|
- name: Ensure C toolchain
|
|
shell: bash
|
|
run: bash ./scripts/ci/ensure_c_toolchain.sh
|
|
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
|
with:
|
|
toolchain: 1.92.0
|
|
components: clippy, rustfmt
|
|
|
|
- name: Ensure C toolchain for Rust builds
|
|
shell: bash
|
|
run: ./scripts/ci/ensure_cc.sh
|
|
|
|
- name: Cache Cargo dependencies
|
|
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v3
|
|
with:
|
|
prefix-key: ci-cd-security-build
|
|
cache-bin: false
|
|
|
|
- name: Build
|
|
shell: bash
|
|
run: cargo build --locked --verbose --all-features
|
|
|
|
- name: Run tests
|
|
shell: bash
|
|
run: cargo test --locked --verbose --all-features
|
|
|
|
- name: Run benchmarks
|
|
shell: bash
|
|
run: cargo bench --locked --verbose
|
|
|
|
- name: Lint with Clippy
|
|
shell: bash
|
|
run: cargo clippy --locked --all-targets --all-features -- -D warnings
|
|
|
|
- name: Check formatting
|
|
shell: bash
|
|
run: cargo fmt -- --check
|
|
|
|
security-scans:
|
|
runs-on: [self-hosted, Linux, X64, blacksmith-2vcpu-ubuntu-2404]
|
|
timeout-minutes: 60
|
|
needs: build-and-test
|
|
permissions:
|
|
contents: read
|
|
security-events: write
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
|
|
- name: Ensure C toolchain
|
|
shell: bash
|
|
run: bash ./scripts/ci/ensure_c_toolchain.sh
|
|
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
|
with:
|
|
toolchain: 1.92.0
|
|
|
|
- name: Ensure C toolchain for Rust builds
|
|
shell: bash
|
|
run: ./scripts/ci/ensure_cc.sh
|
|
|
|
- name: Cache Cargo dependencies
|
|
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v3
|
|
with:
|
|
prefix-key: ci-cd-security-security
|
|
cache-bin: false
|
|
|
|
- name: Install cargo-audit
|
|
shell: bash
|
|
run: cargo install cargo-audit --locked --features=fix
|
|
|
|
- name: Install cargo-deny
|
|
shell: bash
|
|
run: cargo install cargo-deny --locked
|
|
|
|
- name: Dependency vulnerability audit
|
|
shell: bash
|
|
run: cargo audit --deny warnings
|
|
|
|
- name: Dependency license and security check
|
|
shell: bash
|
|
run: cargo deny check
|
|
|
|
- name: Install gitleaks
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
bin_dir="${RUNNER_TEMP}/bin"
|
|
mkdir -p "${bin_dir}"
|
|
bash ./scripts/ci/install_gitleaks.sh "${bin_dir}"
|
|
echo "${bin_dir}" >> "$GITHUB_PATH"
|
|
|
|
- name: Scan for secrets
|
|
shell: bash
|
|
run: gitleaks detect --source=. --verbose --config=.gitleaks.toml
|
|
|
|
- name: Static analysis with Semgrep
|
|
uses: semgrep/semgrep-action@713efdd345f3035192eaa63f56867b88e63e4e5d # v1
|
|
with:
|
|
config: auto
|
|
|
|
fuzz-testing:
|
|
runs-on: [self-hosted, Linux, X64, blacksmith-2vcpu-ubuntu-2404]
|
|
timeout-minutes: 90
|
|
needs: build-and-test
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
target:
|
|
- fuzz_config_parse
|
|
- fuzz_tool_params
|
|
- fuzz_webhook_payload
|
|
- fuzz_provider_response
|
|
- fuzz_command_validation
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
|
|
- name: Ensure C toolchain
|
|
shell: bash
|
|
run: bash ./scripts/ci/ensure_c_toolchain.sh
|
|
|
|
- name: Install Rust nightly
|
|
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
|
|
with:
|
|
toolchain: nightly
|
|
components: llvm-tools-preview
|
|
|
|
- name: Cache Cargo dependencies
|
|
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v3
|
|
with:
|
|
prefix-key: ci-cd-security-fuzz
|
|
cache-bin: false
|
|
|
|
- name: Run fuzz tests
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
cargo install cargo-fuzz --locked
|
|
cargo +nightly fuzz run ${{ matrix.target }} -- -max_total_time=300 -max_len=4096
|
|
|
|
container-build-and-scan:
|
|
runs-on: [self-hosted, Linux, X64, blacksmith-2vcpu-ubuntu-2404]
|
|
timeout-minutes: 45
|
|
needs: security-scans
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
|
|
- name: Set up Blacksmith Docker builder
|
|
uses: useblacksmith/setup-docker-builder@ef12d5b165b596e3aa44ea8198d8fde563eab402 # v1
|
|
|
|
- name: Build Docker image
|
|
uses: useblacksmith/build-push-action@30c71162f16ea2c27c3e21523255d209b8b538c1 # v2
|
|
with:
|
|
context: .
|
|
push: false
|
|
load: true
|
|
tags: ghcr.io/${{ github.repository }}:ci-security
|
|
|
|
- name: Scan Docker image for vulnerabilities
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
docker run --rm \
|
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
|
aquasec/trivy:0.58.2 image \
|
|
--exit-code 1 \
|
|
--no-progress \
|
|
--severity HIGH,CRITICAL \
|
|
ghcr.io/${{ github.repository }}:ci-security
|
|
|
|
publish:
|
|
runs-on: [self-hosted, Linux, X64, blacksmith-2vcpu-ubuntu-2404]
|
|
timeout-minutes: 60
|
|
if: github.event_name == 'release'
|
|
needs:
|
|
- build-and-test
|
|
- security-scans
|
|
- fuzz-testing
|
|
- container-build-and-scan
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
|
|
|
- name: Set up Blacksmith Docker builder
|
|
uses: useblacksmith/setup-docker-builder@ef12d5b165b596e3aa44ea8198d8fde563eab402 # v1
|
|
|
|
- name: Login to GHCR
|
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GHCR_TOKEN }}
|
|
|
|
- name: Build and push Docker image
|
|
uses: useblacksmith/build-push-action@30c71162f16ea2c27c3e21523255d209b8b538c1 # v2
|
|
with:
|
|
context: .
|
|
push: true
|
|
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }},ghcr.io/${{ github.repository }}:latest
|
|
build-args: |
|
|
ZEROCLAW_CARGO_ALL_FEATURES=true
|