diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..bc0d0f94 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,64 @@ +# Dependabot version updates. Docs: https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference +# Every ecosystem carries a 7-day cooldown: no dependency is bumped same-day, so a +# freshly-published malicious release (the "mini shai-hulud" npm-worm class of +# supply-chain attack) has a week to be flagged/yanked before we'd auto-PR it. +version: 2 +updates: + # PRIMARY TARGET: actions are SHA-pinned in .github/workflows; Dependabot keeps + # those pins current (and re-pins to the new SHA), just delayed 7 days. One + # grouped PR keeps the action bumps from flooding the queue. + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + cooldown: + default-days: 7 + open-pull-requests-limit: 5 + groups: + github-actions: + patterns: + - "*" + + # Cargo workspace root: core `ordvec` + the `ordvec-python` binding member + # (single workspace Cargo.lock). Group minor+patch into one PR; majors stay + # separate for manual review. + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" + cooldown: + default-days: 7 + open-pull-requests-limit: 5 + groups: + cargo-minor-patch: + update-types: + - "minor" + - "patch" + + # ordvec-python declared Python deps (numpy). Grouped; no-op if nothing parseable. + - package-ecosystem: "pip" + directory: "/ordvec-python" + schedule: + interval: "weekly" + cooldown: + default-days: 7 + open-pull-requests-limit: 5 + groups: + pip-minor-patch: + update-types: + - "minor" + - "patch" + + # fuzz/ is a workspace-EXCLUDED standalone crate (own Cargo.lock), nightly-only + # cargo-fuzz tooling. Dev-only surface, low priority — group all, tight PR limit. + - package-ecosystem: "cargo" + directory: "/fuzz" + schedule: + interval: "weekly" + cooldown: + default-days: 7 + open-pull-requests-limit: 2 + groups: + fuzz-deps: + patterns: + - "*" diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 3e5d2c30..7c51a03c 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -30,7 +30,10 @@ jobs: name: cargo-deny advisories runs-on: ubuntu-latest steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: EmbarkStudios/cargo-deny-action@a531616d8ce3b9177443e48a1159bc945a099823 # v2.0.19 with: command: check advisories diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index a23c505d..c12f325e 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -40,6 +40,9 @@ jobs: name: release notes (git-cliff) runs-on: ubuntu-latest steps: + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit - name: Gate on a stable SemVer tag id: semver shell: bash @@ -55,10 +58,10 @@ jobs: echo "::notice::$TAG_NAME is not a stable vMAJOR.MINOR.PATCH tag; skipping release notes." echo "ok=false" >> "$GITHUB_OUTPUT" fi - # actions/checkout v4.3.1, SHA-pinned — a contents:write job pins ALL + # actions/checkout v6.0.2, SHA-pinned — a contents:write job pins ALL # tooling to immutable refs (no mutable tags), git-cliff-action included. - if: steps.semver.outputs.ok == 'true' - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # full history + tags for git-cliff - name: Generate release notes diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05f8b7f1..97410f20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,12 @@ jobs: name: lint (fmt + clippy) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + # Establish an egress baseline (audit-only: logs outbound traffic + # without blocking it). A later PR can switch to block + allowlist. + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: clippy, rustfmt @@ -66,7 +71,10 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v4 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: cargo test (default features) @@ -89,7 +97,10 @@ jobs: name: msrv (1.89.0) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@1.89.0 - uses: Swatinem/rust-cache@v2 - name: cargo build @@ -106,7 +117,10 @@ jobs: name: deps (no-system-deps + publish dry-run) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: assert no BLAS/faer/ndarray/statrs in the tree @@ -145,7 +159,10 @@ jobs: name: cargo-deny (advisories + licenses + bans + sources) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: EmbarkStudios/cargo-deny-action@v2 with: command: check @@ -170,7 +187,10 @@ jobs: SDE_VERSION: sde-external-10.7.0-2026-02-18-lin SDE_URL_BASE: https://downloadmirror.intel.com/913594 steps: - - uses: actions/checkout@v4 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: install Intel SDE @@ -238,7 +258,10 @@ jobs: name: wasm32 (simd128) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: targets: wasm32-unknown-unknown, wasm32-wasip1 @@ -279,7 +302,10 @@ jobs: matrix: os: [ubuntu-latest, ubuntu-24.04-arm] steps: - - uses: actions/checkout@v4 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: run bench_rank (scaled, seeded synthetic corpus) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..23747032 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,78 @@ +name: codeql + +# CodeQL code scanning (GitHub's static-analysis / variant-analysis engine). +# Scans the Rust core crate, the PyO3 Python bindings, and the GitHub Actions +# workflows themselves, surfacing security findings as code-scanning alerts. +# +# build-mode: none — CodeQL's Rust support and build-less ("no-build") database +# creation are GA (CodeQL >= 2.23.3). Rust, Python, and `actions` are all +# analysed WITHOUT a compiler/interpreter run, so no `cargo build` (and no +# MSRV toolchain) is needed here; the database is built directly from source. +# This keeps the scan fast and independent of the build matrix. +# +# Least-privilege permissions: `security-events: write` is the only elevated +# scope (required to upload SARIF / publish alerts); `contents: read` + +# `actions: read` are read-only. Nothing else is granted. +# +# Because this workflow runs UNATTENDED on a cron schedule AND holds +# `security-events: write`, every third-party action is SHA-pinned (immutable) +# — a compromised mutable tag (@v4/@v6) cannot auto-execute on a scheduled, +# write-scoped runner. harden-runner audits egress as the first step. + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Weekly. Wednesday 07:42 UTC — deliberately off the audit.yml slot + # (Monday 06:17 UTC) so the two scheduled scans never collide. + - cron: "42 7 * * 3" + +# Least-privilege: read-only at the workflow level; the analyze job elevates to +# security-events: write (the minimum CodeQL needs to publish alerts). +permissions: + contents: read + +concurrency: + group: codeql-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + # Skip CodeQL on PRs from forks: a forked PR gets a read-only GITHUB_TOKEN, + # so the SARIF upload (needs security-events: write) can't succeed and would + # fail noisily. push-to-main + the weekly schedule still scan everything that + # lands, and same-repo PRs still get PR-time scanning. (CodeQL is also not a + # required status check, so a fork PR is never blocked either way.) + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + permissions: + security-events: write # upload SARIF / publish code-scanning alerts + contents: read # checkout source + actions: read # read workflow run metadata (required by CodeQL) + strategy: + fail-fast: false + matrix: + language: [rust, python, actions] + steps: + - name: Harden runner + uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4 + with: + languages: ${{ matrix.language }} + # No-build database creation (GA for rust/python/actions). + build-mode: none + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index f5d68988..b1ad1574 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -36,7 +36,10 @@ jobs: name: clippy + fmt (binding) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: clippy, rustfmt @@ -73,8 +76,11 @@ jobs: - { os: macos-latest, python: "3.13" } # macOS arm64 — NEON - { os: windows-latest, python: "3.13" } # windows x64 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 + with: + egress-policy: audit + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python }} - uses: dtolnay/rust-toolchain@stable