name: Replacement security gates
run-name: Replacement security gates ${{ inputs.source_sha || github.sha }}
on:
push:
branches:
- main
paths:
- src/**
- ffi/**
- nss/**
- fuzz/**
- build.rs
- Cargo.toml
- Cargo.lock
- .github/workflows/replacement-security-gates.yml
workflow_dispatch:
inputs:
source_sha:
description: Full Git commit to test; blank selects the dispatch ref
required: false
type: string
schedule:
- cron: '43 4 * * 3'
permissions:
contents: read
statuses: write
concurrency:
group: replacement-security-${{ github.ref }}
cancel-in-progress: true
jobs:
fuzz:
name: libFuzzer corpus and smoke
runs-on: ubuntu-24.04
timeout-minutes: 40
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 with:
ref: ${{ inputs.source_sha || github.sha }}
- name: Verify exact source identity
shell: bash
env:
REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
run: |
set -euo pipefail
[[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
- name: Install libFuzzer toolchain
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
clang libssl-dev liburing-dev
rustup toolchain install nightly-2025-02-15 --profile minimal
cargo +nightly-2025-02-15 install cargo-fuzz --version 0.12.0 --locked
- name: Run libFuzzer smoke on every target
shell: bash
run: |
set -euo pipefail
test -s fuzz/Cargo.toml
cargo +nightly-2025-02-15 fuzz build
mapfile -t targets < <(cargo +nightly-2025-02-15 fuzz list)
test "${#targets[@]}" -gt 0
for target in "${targets[@]}"; do
timeout --signal=TERM --kill-after=5s 45s \
cargo +nightly-2025-02-15 fuzz run "$target" -- \
-max_total_time=30 -timeout=5 -rss_limit_mb=2048
done
- name: Upload fuzz failures
if: ${{ failure() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with:
name: fuzz-failures-${{ inputs.source_sha || github.sha }}
path: fuzz/artifacts
if-no-files-found: ignore
retention-days: 14
asan:
name: Address Sanitizer ASan
runs-on: ubuntu-24.04
timeout-minutes: 35
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 with:
ref: ${{ inputs.source_sha || github.sha }}
- name: Verify exact source identity
shell: bash
env:
REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
run: |
set -euo pipefail
[[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
- name: Install Address Sanitizer dependencies
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
clang gfortran libssl-dev liburing-dev
- name: Run Address Sanitizer ASan
shell: bash
env:
ASAN_OPTIONS: abort_on_error=1:detect_leaks=1:strict_string_checks=1:check_initialization_order=1
run: |
set -euo pipefail
make clean
make check-native \
CC='clang -fsanitize=address -fno-omit-frame-pointer' \
FC='gfortran -fsanitize=address -fno-omit-frame-pointer' \
CFLAGS='-O1 -g -fPIC -std=c17 -Wall -Wextra -Werror -Wpedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
LDLIBS='-lssl -lcrypto'
make -C nss clean
make -C nss all test_nss \
CC='clang -fsanitize=address -fno-omit-frame-pointer' \
CFLAGS='-O1 -g -fPIC -std=c17 -Wall -Wextra -Werror -Wpedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2'
NSS_TEST_GETENT_LD_PRELOAD="$(clang -print-file-name=libclang_rt.asan-x86_64.so)"
test -r "$NSS_TEST_GETENT_LD_PRELOAD"
export NSS_TEST_GETENT_LD_PRELOAD
bash nss/run-tests.sh
ubsan:
name: Undefined Behavior Sanitizer UBSan
runs-on: ubuntu-24.04
timeout-minutes: 35
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 with:
ref: ${{ inputs.source_sha || github.sha }}
- name: Verify exact source identity
shell: bash
env:
REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
run: |
set -euo pipefail
[[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
- name: Install Undefined Behavior Sanitizer dependencies
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
clang gfortran libssl-dev liburing-dev
- name: Run Undefined Behavior Sanitizer UBSan
shell: bash
env:
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: |
set -euo pipefail
make clean
make check-native \
CC='clang -fsanitize=undefined -fno-omit-frame-pointer' \
CFLAGS='-O1 -g -fPIC -std=c17 -Wall -Wextra -Werror -Wpedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2' \
LDLIBS='-lssl -lcrypto -lubsan'
make -C nss clean
make -C nss all test_nss \
CC='clang -fsanitize=undefined -fno-omit-frame-pointer' \
CFLAGS='-O1 -g -fPIC -std=c17 -Wall -Wextra -Werror -Wpedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2'
NSS_TEST_GETENT_LD_PRELOAD="$(clang -print-file-name=libclang_rt.ubsan_standalone-x86_64.so)"
test -r "$NSS_TEST_GETENT_LD_PRELOAD"
export NSS_TEST_GETENT_LD_PRELOAD
bash nss/run-tests.sh
miri:
name: Miri strict provenance
runs-on: ubuntu-24.04
timeout-minutes: 35
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 with:
ref: ${{ inputs.source_sha || github.sha }}
- name: Verify exact source identity
shell: bash
env:
REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
run: |
set -euo pipefail
[[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
- name: Install Miri
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
gfortran libssl-dev liburing-dev
rustup toolchain install nightly-2025-02-15 --profile minimal --component miri,rust-src
cargo +nightly-2025-02-15 miri setup
- name: Run Miri strict provenance
env:
MIRIFLAGS: -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation
run: cargo +nightly-2025-02-15 miri test --lib miri_
tsan:
name: Thread Sanitizer TSan
runs-on: ubuntu-24.04
timeout-minutes: 55
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 with:
ref: ${{ inputs.source_sha || github.sha }}
- name: Verify exact source identity
shell: bash
env:
REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
run: |
set -euo pipefail
[[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
- name: Install Thread Sanitizer toolchain
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
gfortran libssl-dev liburing-dev
rustup toolchain install nightly-2025-02-15 --profile minimal --component rust-src
- name: Run Thread Sanitizer TSan
shell: bash
env:
RUSTFLAGS: -Zsanitizer=thread
RUSTDOCFLAGS: -Zsanitizer=thread
TSAN_OPTIONS: halt_on_error=1:second_deadlock_stack=1
run: |
cargo +nightly-2025-02-15 test \
-Zbuild-std \
--target x86_64-unknown-linux-gnu \
--lib --all-features --locked
valgrind:
name: Valgrind NSS Varlink and DNS fallback
runs-on: ubuntu-24.04
timeout-minutes: 40
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 with:
ref: ${{ inputs.source_sha || github.sha }}
- name: Verify exact source identity
shell: bash
env:
REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
run: |
set -euo pipefail
[[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
test "$(git rev-parse HEAD)" = "$REQUESTED_SHA"
- name: Install Valgrind dependencies
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
gfortran libssl-dev liburing-dev valgrind
- name: Run Valgrind NSS Varlink and DNS fallback
shell: bash
run: |
set -euo pipefail
make -C nss clean all test_nss
mv nss/test_nss nss/test_nss.real
cat > nss/test_nss <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
exec valgrind \
--error-exitcode=99 \
--leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--errors-for-leak-kinds=definite,indirect,possible \
"$root/test_nss.real" "$@"
EOF
chmod 0755 nss/test_nss
bash nss/run-tests.sh
report:
name: Publish replacement security status
if: ${{ always() }}
needs:
- fuzz
- asan
- ubsan
- miri
- tsan
- valgrind
runs-on: ubuntu-24.04
steps:
- name: Publish exact-source aggregate status
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ inputs.source_sha || github.sha }}
FUZZ_RESULT: ${{ needs.fuzz.result }}
ASAN_RESULT: ${{ needs.asan.result }}
UBSAN_RESULT: ${{ needs.ubsan.result }}
MIRI_RESULT: ${{ needs.miri.result }}
TSAN_RESULT: ${{ needs.tsan.result }}
VALGRIND_RESULT: ${{ needs.valgrind.result }}
run: |
set -euo pipefail
state=success
for result in \
"$FUZZ_RESULT" "$ASAN_RESULT" "$UBSAN_RESULT" \
"$MIRI_RESULT" "$TSAN_RESULT" "$VALGRIND_RESULT"; do
if [[ "$result" != success ]]; then
state=failure
fi
done
payload=$(printf '{"state":"%s","context":"replacement/security","description":"Replacement security gates %s","target_url":"%s"}' "$state" "$state" "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}")
curl --fail-with-body --silent --show-error -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/statuses/${SOURCE_SHA}" \
--data "$payload" >/dev/null