agentic/code/frameworks/security-engineering/skills/sanitizer-in-ci/SKILL.md
Detect language/toolchain and emit CI job recipes that build with runtime sanitizers (ASan/UBSan/MSan/TSan, race detectors, faulthandler)
npx skillsauth add jmagly/aiwg sanitizer-in-ciInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are the Sanitizer Integration Engineer — detect what the project is built in, what its CI looks like, and emit recipes that wire compile-time sanitizers and runtime checkers into PR-gating jobs.
"Catch the class of bugs the type system doesn't." Sanitizers find memory safety violations, undefined behavior, races, and unsafe deserialization at test time — finding bugs months before they become CVEs. The cost is one extra CI job per language; the value is every memory-safety incident you don't have.
| Language | Sanitizers | Notes |
|----------|-----------|-------|
| C / C++ | ASan, UBSan, MSan, TSan | Compiler-driven; Clang preferred over GCC for breadth |
| Rust | RUSTFLAGS=-Zsanitizer=..., miri (interpreter-mode UB checks) | Nightly required for compile-time sanitizers; miri runs on stable |
| Go | go test -race (race detector), go build -msan (Linux/AMD64 only) | Race detector is the default; MSan needs CGO |
| Python | PYTHONFAULTHANDLER=1, -X dev, -W error (warnings-as-errors) | Plus pytest -W error::DeprecationWarning |
| Node.js | --use-strict, --throw-deprecation, --unhandled-rejections=strict, --experimental-vm-modules | No memory sanitizer; rely on UB caught by V8 + strict-mode flags |
Cycle-2 additions targeted: Swift, Java/JVM (HotSpot JFR + JNI checks), Ruby.
Use the shared detection helper:
# agentic/code/frameworks/security-engineering/lib/toolchain-detect.sh
detect_languages # outputs newline-separated language codes
Detection signals (per language):
c — presence of Makefile, CMakeLists.txt, *.c/*.h in srccpp — CMakeLists.txt with CXX, *.cpp/*.cc/*.hpp, meson.buildrust — Cargo.tomlgo — go.modpython — pyproject.toml, setup.py, requirements*.txtnode — package.json with dependencies or devDependencies.github/workflows/ → GitHub Actions.gitea/workflows/ → Gitea Actions (same syntax as GitHub).gitlab-ci.yml → GitLab CIIf unclear, emit recipes for all three with a chooser comment.
For each detected language, write to .aiwg/security-engineering/sanitizers/{ci-platform}/{language}.yaml.
Reference emitter:
agentic/code/frameworks/security-engineering/skills/sanitizer-in-ci/scripts/emit.sh \
--language auto --ci auto
# .aiwg/security-engineering/sanitizers/github/c.yaml
# Add to your workflow OR copy into .github/workflows/sanitizers.yml
name: Sanitizers (C/C++)
on:
pull_request:
push:
branches: [main]
jobs:
asan-ubsan:
runs-on: ubuntu-latest
container: node:24@sha256:050bf2bbe33c1d6754e060bec89378a79ed831f04a7bb1a53fe45e997df7b3bb # 24.15.0
env:
CC: clang
CXX: clang++
CFLAGS: "-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=all"
CXXFLAGS: "-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=all"
LDFLAGS: "-fsanitize=address,undefined"
ASAN_OPTIONS: "abort_on_error=1:print_stacktrace=1:halt_on_error=1:detect_leaks=1"
UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
LSAN_OPTIONS: "suppressions=.aiwg/security-engineering/sanitizers/lsan-suppressions.txt"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Install clang
run: apt-get update && apt-get install -y clang make cmake
- name: Build with ASan + UBSan
run: |
# Adjust to your build system:
# cmake: cmake -B build && cmake --build build
# make: make
make
- name: Run test suite under sanitizers
run: make test
- name: Upload sanitizer logs on failure
if: failure()
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: sanitizer-logs-c
path: |
build/Testing/Temporary/LastTest.log
*.san.log
msan:
# MSan requires every dependency to be MSan-instrumented (including libc).
# Most projects find this prohibitively expensive — enable only if you can build deps from source.
runs-on: ubuntu-latest
if: false # set to true if your build allows MSan-instrumented deps
# ... similar shape with -fsanitize=memory
tsan:
# TSan: thread races. Enable for multi-threaded code.
runs-on: ubuntu-latest
if: false # enable for threaded projects
# ... similar shape with -fsanitize=thread
# .aiwg/security-engineering/sanitizers/github/rust.yaml
name: Sanitizers (Rust)
on: [pull_request, push]
jobs:
asan:
runs-on: ubuntu-latest
env:
RUSTFLAGS: "-Zsanitizer=address"
RUSTDOCFLAGS: "-Zsanitizer=address"
ASAN_OPTIONS: "abort_on_error=1:halt_on_error=1"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- name: Install nightly toolchain
run: |
rustup toolchain install nightly --component rust-src
rustup default nightly
- name: Test with ASan
run: cargo +nightly test -Zbuild-std --target x86_64-unknown-linux-gnu
miri:
runs-on: ubuntu-latest
env:
MIRIFLAGS: "-Zmiri-strict-provenance"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- name: Install miri
run: |
rustup toolchain install nightly --component miri
cargo +nightly miri setup
- name: Run miri
run: cargo +nightly miri test
# .aiwg/security-engineering/sanitizers/github/go.yaml
name: Sanitizers (Go)
on: [pull_request, push]
jobs:
race-detector:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0
with:
go-version-file: go.mod
- name: Test with race detector
run: go test -race -count=1 ./...
# .aiwg/security-engineering/sanitizers/github/python.yaml
name: Runtime Checks (Python)
on: [pull_request, push]
jobs:
faulthandler-and-warnings:
runs-on: ubuntu-latest
env:
PYTHONFAULTHANDLER: "1"
PYTHONDEVMODE: "1" # equivalent to python -X dev
PYTHONWARNINGS: "error" # warnings-as-errors
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version-file: .python-version
- name: Install deps
run: pip install -e .[dev]
- name: Test with dev mode + faulthandler
run: pytest -W error
For C/C++, write .aiwg/security-engineering/sanitizers/lsan-suppressions.txt with documented categories:
# LeakSanitizer suppressions
# Each entry MUST have a reason comment. Unjustified entries are reviewed quarterly.
# Third-party library known leak (upstream tracker: <link>)
# leak:libfoo
# OpenSSL one-time init leak — by design, lives until process exit
# leak:OPENSSL_init_crypto
.aiwg/security-engineering/sanitizers/OPERATOR.md:
# Sanitizer Operator Guide
## What each sanitizer catches
- **ASan (AddressSanitizer)**: heap/stack overflow, use-after-free, double-free, memory leaks
- **UBSan (UndefinedBehaviorSanitizer)**: signed overflow, null deref, OOB shifts, misaligned reads
- **MSan (MemorySanitizer)**: uninitialized memory reads
- **TSan (ThreadSanitizer)**: data races, deadlocks
- **Race (Go)**: data races in goroutines
- **faulthandler (Python)**: segfaults in C extensions; emits Python tracebacks
## Triage workflow
1. Sanitizer fails in CI → download the log artifact
2. Identify the access (e.g., `READ of size 4 at 0x...`)
3. Match to source via the stack trace
4. Decide: real bug → fix; false positive → suppression entry with link to upstream issue
## Suppressions policy
Every suppression entry MUST cite a reason. Quarterly review removes obsolete ones.
dev-idempotent-builds.md rule — sanitizer CI jobs MUST use pinned action SHAs and pinned container digestsci-action-pinning.md rule — samefuzzing-in-ci skill — complementary; fuzzing generates inputs that sanitizers then validatescripts/emit.sh emits recipe files under .aiwg/security-engineering/sanitizers/{ci-platform}/.fuzzing-in-ci, not here..aiwg/security/curl-checklist-gap-analysis.md row 14data-ai
Report which research-corpus radar sidecars are overdue for refresh. Computes staleness (days since last refresh vs the cadence window) for every radar, sorted most-overdue-first. Runs via `aiwg corpus radar-status`.
data-ai
Aggregate research-corpus radar sidecars into a corpus or per-cluster freshness report — totals, overdue count, per-cluster / per-GRADE / per-trajectory breakdowns, an overdue table, and per-radar rationale snippets. Runs via `aiwg corpus radar-report`.
testing
Scaffold radar/freshness sidecars for research-corpus REFs. Pulls title/authors from the citation sidecar and GRADE from the analysis doc, defaults the refresh cadence from GRADE and the cluster from a corpus-local map, and stamps documentation/radar/REF-XXX-radar.md. Runs via `aiwg corpus radar-init`.
data-ai
Compute an entity's publication trajectory — per-year paper counts, topic drift, hot-streak detection (≥3 consecutive A-grade years), and career phase. Runs via `aiwg corpus profile-temporal`.