-
Notifications
You must be signed in to change notification settings - Fork 0
[codex] Add nightly security sweep #195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,276 @@ | ||
| name: Nightly Security | ||
|
|
||
| on: | ||
| schedule: | ||
| - cron: "41 2 * * *" | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| env: | ||
| CARGO_FUZZ_VERSION: "0.13.2" | ||
| CARGO_TERM_COLOR: always | ||
|
|
||
| jobs: | ||
| changes: | ||
| name: Changed security surfaces | ||
| runs-on: ubuntu-24.04 | ||
| outputs: | ||
| run: ${{ steps.classify.outputs.run }} | ||
| head_sha: ${{ steps.classify.outputs.head_sha }} | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | ||
| with: | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
| submodules: false | ||
|
|
||
| - name: Restore last successful nightly state | ||
| uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 | ||
| with: | ||
| path: .nightly-security-state | ||
| key: nightly-security-state-${{ github.run_id }}-${{ github.run_attempt }} | ||
| restore-keys: | | ||
| nightly-security-state- | ||
|
|
||
| - name: Classify changed paths | ||
| id: classify | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| head_sha="$(git rev-parse HEAD)" | ||
| run=false | ||
|
|
||
| changed_files="${RUNNER_TEMP}/registry-stack-nightly-security-files" | ||
| : > "${changed_files}" | ||
|
|
||
| is_relevant_path() { | ||
| local path="$1" | ||
| case "${path}" in | ||
| .github/workflows/*|.github/dependabot.yml) | ||
| return 0 | ||
| ;; | ||
| Cargo.toml|Cargo.lock|deny.toml|rust-toolchain*) | ||
| return 0 | ||
| ;; | ||
| crates/registry-relay/*|crates/registry-notary*/*) | ||
| return 0 | ||
| ;; | ||
| crates/registry-platform-authcommon/*|crates/registry-platform-crypto/*) | ||
| return 0 | ||
| ;; | ||
| crates/registry-platform-oid4vci/*|crates/registry-platform-replay/*|crates/registry-platform-sdjwt/*) | ||
| return 0 | ||
| ;; | ||
| products/notary/*|products/platform/*|release/docker/*) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the only relevant change is under Useful? React with 👍 / 👎.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed. Fixed in 5bfe3ef: the nightly sweep now scans |
||
| return 0 | ||
| ;; | ||
| *) | ||
| return 1 | ||
| ;; | ||
| esac | ||
| } | ||
|
|
||
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then | ||
| run=true | ||
| elif [[ ! -f .nightly-security-state/last-success-sha ]]; then | ||
| run=true | ||
| else | ||
| base_sha="$(tr -d '[:space:]' < .nightly-security-state/last-success-sha)" | ||
| if [[ -z "${base_sha}" ]] || | ||
| ! git cat-file -e "${base_sha}^{commit}" 2>/dev/null; then | ||
| run=true | ||
| elif [[ "${base_sha}" != "${head_sha}" ]]; then | ||
| git diff --name-only -z "${base_sha}" "${head_sha}" > "${changed_files}" | ||
| while IFS= read -r -d '' path; do | ||
| if is_relevant_path "${path}"; then | ||
| run=true | ||
| break | ||
| fi | ||
| done < "${changed_files}" | ||
| fi | ||
| fi | ||
|
|
||
| { | ||
| echo "run=${run}" | ||
| echo "head_sha=${head_sha}" | ||
| } >> "${GITHUB_OUTPUT}" | ||
|
|
||
| assurance: | ||
| name: Security assurance manifests | ||
| needs: changes | ||
| if: needs.changes.outputs.run == 'true' | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | ||
| with: | ||
| persist-credentials: false | ||
| submodules: false | ||
|
|
||
| - name: Test assurance ratchets | ||
| run: | | ||
| python3 -m unittest \ | ||
| crates/registry-relay/tests/security_assurance_check_test.py \ | ||
| crates/registry-relay/tests/advisory_baseline_check_test.py \ | ||
| products/notary/tests/security_assurance_check_test.py \ | ||
| products/notary/tests/advisory_baseline_check_test.py | ||
|
|
||
| - name: Relay exposure and container checks | ||
| working-directory: crates/registry-relay | ||
| run: | | ||
| python3 scripts/check_security_assurance.py manifest | ||
| python3 scripts/check_security_assurance.py dockerfile-secrets | ||
|
Comment on lines
+125
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the only relevant change is a Relay OpenAPI/security-assurance doc update such as Useful? React with 👍 / 👎. |
||
|
|
||
| - name: Notary container and OpenAPI checks | ||
| working-directory: products/notary | ||
| run: | | ||
| python3 scripts/check_security_assurance.py manifest | ||
| python3 scripts/check_security_assurance.py dockerfile-secrets | ||
| python3 scripts/check_security_assurance.py openapi-baseline | ||
|
Comment on lines
+131
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the only relevant change is under Useful? React with 👍 / 👎. |
||
|
|
||
| - name: Release image Dockerfile checks | ||
| run: | | ||
| python3 - <<'PY' | ||
| import re | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| secret_copy_re = re.compile( | ||
| r"\b(?:COPY|ADD)\b(?=.*(?:\.env|\.pem|\.key|\.p12|jwk|secret|credential))|\"d\"\s*:", | ||
| re.IGNORECASE, | ||
| ) | ||
| paths = sorted(Path("release/docker").glob("Dockerfile.registry-*")) | ||
| if not paths: | ||
| print("security assurance check failed: no release Dockerfiles found", file=sys.stderr) | ||
| sys.exit(1) | ||
| for path in paths: | ||
| for lineno, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): | ||
| if secret_copy_re.search(line): | ||
| print( | ||
| f"security assurance check failed: {path}:{lineno} may copy secret or private JWK material", | ||
| file=sys.stderr, | ||
| ) | ||
| sys.exit(1) | ||
| PY | ||
|
|
||
| notary-fuzz: | ||
| name: Notary fuzz smoke | ||
| needs: changes | ||
| if: needs.changes.outputs.run == 'true' | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 45 | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | ||
| with: | ||
| persist-credentials: false | ||
| submodules: false | ||
|
|
||
| - name: Install Rust nightly | ||
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # nightly | ||
| with: | ||
| toolchain: nightly | ||
|
|
||
| - name: Cache Cargo registry and build artifacts | ||
| uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | ||
|
|
||
| - name: Install cargo-fuzz | ||
| uses: taiki-e/install-action@25435dc8dd3baed7417e0c96d3fe89013a5b2e09 # v2.81.3 | ||
| with: | ||
| tool: cargo-fuzz@${{ env.CARGO_FUZZ_VERSION }} | ||
|
|
||
| - name: Run request parser fuzz smoke | ||
| working-directory: products/notary | ||
| run: | | ||
| set -euo pipefail | ||
| mkdir -p fuzz/artifacts/core_request_bodies | ||
| cargo +nightly fuzz run --fuzz-dir fuzz --target x86_64-unknown-linux-gnu core_request_bodies -- \ | ||
| -max_total_time=120 \ | ||
| -rss_limit_mb=1024 \ | ||
| -artifact_prefix=fuzz/artifacts/core_request_bodies/ \ | ||
| -print_final_stats=1 | ||
|
|
||
| - name: Upload notary fuzz artifacts | ||
| if: failure() | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: nightly-notary-fuzz-artifacts | ||
| path: products/notary/fuzz/artifacts | ||
| if-no-files-found: ignore | ||
|
|
||
| platform-fuzz: | ||
| name: Platform fuzz smoke | ||
| needs: changes | ||
| if: needs.changes.outputs.run == 'true' | ||
| runs-on: ubuntu-24.04 | ||
| timeout-minutes: 60 | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | ||
| with: | ||
| persist-credentials: false | ||
| submodules: false | ||
|
|
||
| - name: Install Rust nightly | ||
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # nightly | ||
| with: | ||
| toolchain: nightly | ||
|
|
||
| - name: Cache Cargo registry and build artifacts | ||
| uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | ||
|
|
||
| - name: Install cargo-fuzz | ||
| uses: taiki-e/install-action@25435dc8dd3baed7417e0c96d3fe89013a5b2e09 # v2.81.3 | ||
| with: | ||
| tool: cargo-fuzz@${{ env.CARGO_FUZZ_VERSION }} | ||
|
|
||
| - name: Run parser and credential fuzz smoke | ||
| working-directory: products/platform | ||
| run: | | ||
| set -euo pipefail | ||
| for target in \ | ||
| authcommon_parsers \ | ||
| oid4vci_request_and_proof \ | ||
| sdjwt_holder_proof \ | ||
| sdjwt_issuance | ||
| do | ||
| mkdir -p "fuzz/artifacts/${target}" | ||
| cargo +nightly fuzz run --fuzz-dir fuzz --target x86_64-unknown-linux-gnu "${target}" -- \ | ||
| -max_total_time=60 \ | ||
| -rss_limit_mb=1024 \ | ||
| -artifact_prefix="fuzz/artifacts/${target}/" \ | ||
| -print_final_stats=1 | ||
| done | ||
|
|
||
| - name: Upload platform fuzz artifacts | ||
| if: failure() | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: nightly-platform-fuzz-artifacts | ||
| path: products/platform/fuzz/artifacts | ||
| if-no-files-found: ignore | ||
|
|
||
| save-state: | ||
| name: Save successful nightly state | ||
| needs: | ||
| - changes | ||
| - assurance | ||
| - notary-fuzz | ||
| - platform-fuzz | ||
| if: needs.changes.outputs.run == 'true' | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - name: Write successful head | ||
| run: | | ||
| mkdir -p .nightly-security-state | ||
| printf '%s\n' "${{ needs.changes.outputs.head_sha }}" > .nightly-security-state/last-success-sha | ||
|
|
||
| - name: Save last successful nightly state | ||
| uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 | ||
| with: | ||
| path: .nightly-security-state | ||
| key: nightly-security-state-${{ github.run_id }}-${{ github.run_attempt }} | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a scheduled run only changes
crates/registry-platform-cache/..., this path set falls through and the sweep is skipped, even thoughregistry-platform-replaybuilds its nonce/replay stores directly onCacheStore/compare_and_setfrom that crate and OID4VCI pulls replay into the fuzz dependency graph. In that case a cache API or behavior change that affects nonce replay protection can land without even rebuilding/running the nightly OID4VCI/Notary smoke for that SHA; addcrates/registry-platform-cache/*to this platform group or otherwise cover it.Useful? React with 👍 / 👎.