diff --git a/.github/actionlint-matcher.json b/.github/actionlint-matcher.json new file mode 100644 index 0000000000..4613e1617b --- /dev/null +++ b/.github/actionlint-matcher.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "actionlint", + "pattern": [ + { + "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + "code": 5 + } + ] + } + ] +} diff --git a/.github/actionlint-sarif-template.txt b/.github/actionlint-sarif-template.txt new file mode 100644 index 0000000000..01c589e218 --- /dev/null +++ b/.github/actionlint-sarif-template.txt @@ -0,0 +1,63 @@ +{ + "$schema": "https://json.schemastore.org/sarif-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "Actionlint", + "version": {{ getVersion | json }}, + "informationUri": "https://github.com/rhysd/actionlint", + "rules": [ + {{$first := true}} + {{range $ := allKinds}} + {{if $first}}{{$first = false}}{{else}},{{end}} + { + "id": {{json $.Name}}, + "name": {{$.Name | toPascalCase | json}}, + "defaultConfiguration": { + "level": "warning" + }, + "fullDescription": { + "text": {{json $.Description}} + }, + "helpUri": "https://github.com/rhysd/actionlint/blob/v1.7.12/docs/checks.md" + } + {{end}} + ] + } + }, + "results": [ + {{$first := true}} + {{range $ := .}} + {{if $first}}{{$first = false}}{{else}},{{end}} + { + "ruleId": {{json $.Kind}}, + "level": "warning", + "message": { + "text": {{json $.Message}} + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": {{json $.Filepath}}, + "uriBaseId": "%SRCROOT%" + }, + "region": { + "startLine": {{$.Line}}, + "startColumn": {{$.Column}}, + "endColumn": {{$.EndColumn}}, + "snippet": { + "text": {{json $.Snippet}} + } + } + } + } + ] + } + {{end}} + ] + } + ] +} diff --git a/.github/actionlint.yml b/.github/actionlint.yml new file mode 100644 index 0000000000..35aa72ea78 --- /dev/null +++ b/.github/actionlint.yml @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +self-hosted-runner: + labels: + - linux-amd64-cpu8 + - linux-amd64-gpu-rtxpro6000-latest-1 + - linux-arm64-cpu8 + - linux-arm64-gpu-l4-latest-1 + - nv + - ubuntu-26.04 + - windows-arm64 + - wsl-amd64-gpu-rtxpro6000-latest-1 + +paths: + .github/workflows/windows-msvc.yml: + ignore: + - 'constant expression "false" in condition' diff --git a/.github/actions/setup-nix/action.yml b/.github/actions/setup-nix/action.yml new file mode 100644 index 0000000000..3cd6940c05 --- /dev/null +++ b/.github/actions/setup-nix/action.yml @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Setup Nix +description: Install Nix and configure the OpenShell Cachix cache + +inputs: + cachix-auth-token: + description: Token used to write build outputs to Cachix + required: false + default: "" + +runs: + using: composite + steps: + - uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1 + with: + github_access_token: ${{ github.token }} + + - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + with: + name: openshell + authToken: ${{ inputs.cachix-auth-token }} + skipPush: ${{ inputs.cachix-auth-token == '' }} diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 0000000000..3953988b89 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: OpenShell Rust and SDKs + +paths: + - crates + - examples + - sdk/go + - sdk/typescript/src + - python/openshell + +paths-ignore: + - python/openshell/_proto + - sdk/typescript/src/gen diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..28057fe18a --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,128 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: CodeQL + +on: + pull_request: + merge_group: + types: [checks_requested] + push: + branches: [main] + schedule: + - cron: "29 5 * * 6" + workflow_dispatch: + +permissions: + contents: read + security-events: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: CodeQL (${{ matrix.language }}) + runs-on: ubuntu-latest + timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + include: + - language: rust + build-mode: none + - language: go + build-mode: manual + - language: python + build-mode: none + - language: javascript-typescript + build-mode: none + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Go + if: matrix.language == 'go' + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: sdk/go/go.mod + cache-dependency-path: sdk/go/go.sum + + - name: Initialize CodeQL + uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + config-file: ./.github/codeql/codeql-config.yml + + - name: Build Go SDK + if: matrix.language == 'go' + working-directory: sdk/go + run: go build ./... + + - name: Analyze + id: analyze + uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + category: /language:${{ matrix.language }} + output: codeql-results + upload: never + + - name: Summarize findings + if: always() + env: + LANGUAGE: ${{ matrix.language }} + shell: bash + run: | + set -euo pipefail + shopt -s globstar nullglob + sarif_files=(codeql-results/**/*.sarif) + + { + echo "### CodeQL: $LANGUAGE" + echo + if [ "${#sarif_files[@]}" -eq 0 ]; then + echo "No SARIF report was produced." + else + finding_count=$(jq -s '[.[].runs[]?.results[]?] | length' "${sarif_files[@]}") + echo "Findings: $finding_count" + echo + echo "Findings are informational and do not fail CI." + fi + } >> "$GITHUB_STEP_SUMMARY" + + - name: Upload SARIF to Code Scanning + if: steps.analyze.outcome == 'success' + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + sarif_file: codeql-results + category: /language:${{ matrix.language }} + + - name: Upload SARIF + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: codeql-${{ matrix.language }}-${{ github.run_id }} + path: codeql-results + if-no-files-found: ignore + retention-days: 14 + + result: + name: OpenShell / CodeQL (informational) + if: always() + needs: analyze + runs-on: ubuntu-latest + permissions: {} + steps: + - name: Evaluate analyzer execution + env: + ANALYZE_RESULT: ${{ needs.analyze.result }} + shell: bash + run: | + if [ "$ANALYZE_RESULT" != "success" ]; then + echo "::error::One or more CodeQL analyzers did not complete successfully." + exit 1 + fi + echo "All CodeQL analyzers completed; findings remain informational." diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000000..fc162fd2cb --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,98 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Dependency Review + +on: + pull_request: + merge_group: + types: [checks_requested] + workflow_dispatch: + inputs: + base_sha: + description: Base commit SHA to compare + required: true + type: string + head_sha: + description: Head commit SHA to compare + required: true + type: string + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + review: + name: Dependency Review (informational) + runs-on: ubuntu-latest + env: + BASE_REF: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || inputs.base_sha }} + HEAD_REF: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha || inputs.head_sha }} + steps: + - name: Check Dependency Graph availability + id: preflight + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 + with: + script: | + const shaPattern = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i; + const baseRef = process.env.BASE_REF; + const headRef = process.env.HEAD_REF; + + if (!shaPattern.test(baseRef) || !shaPattern.test(headRef)) { + core.setFailed("Dependency Review requires base and head commit SHAs."); + return; + } + + try { + await github.request( + "GET /repos/{owner}/{repo}/dependency-graph/compare/{basehead}", + { + owner: context.repo.owner, + repo: context.repo.repo, + basehead: `${baseRef}...${headRef}`, + headers: { + "X-GitHub-Api-Version": "2022-11-28", + }, + }, + ); + core.setOutput("available", "true"); + } catch (error) { + const status = error.status; + if (status === 403 || status === 404) { + core.setOutput("available", "false"); + core.warning( + `GitHub Dependency Graph is unavailable (HTTP ${status}); Dependency Review is skipped.`, + ); + await core.summary + .addHeading("Dependency Review", 3) + .addRaw(`GitHub Dependency Graph is unavailable (HTTP ${status}).`, true) + .addRaw( + "The informational review will start automatically once the repository feature is available.", + true, + ) + .write(); + return; + } + + const statusSuffix = status ? ` with HTTP ${status}` : ""; + core.setFailed( + `Dependency Graph preflight failed${statusSuffix}: ${error.message}`, + ); + } + + - name: Review dependency changes + if: steps.preflight.outputs.available == 'true' + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + with: + base-ref: ${{ env.BASE_REF }} + head-ref: ${{ env.HEAD_REF }} + fail-on-severity: high + fail-on-scopes: runtime, development, unknown + warn-only: true + comment-summary-in-pr: never + license-check: false + show-openssf-scorecard: false diff --git a/.github/workflows/workflow-security.yml b/.github/workflows/workflow-security.yml new file mode 100644 index 0000000000..7a87e15483 --- /dev/null +++ b/.github/workflows/workflow-security.yml @@ -0,0 +1,185 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Workflow Security Reports + +on: + pull_request: + merge_group: + types: [checks_requested] + push: + branches: [main] + schedule: + - cron: "17 6 * * 1" + workflow_dispatch: + +permissions: + contents: read + +defaults: + run: + shell: nix develop --command bash -euo pipefail {0} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + actionlint: + name: Actionlint (informational) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Nix + uses: ./.github/actions/setup-nix + with: + cachix-auth-token: ${{ github.event_name != 'pull_request' && secrets.CACHIX_AUTH_TOKEN || '' }} + + - name: Run Actionlint + run: | + set -uo pipefail + mkdir -p reports + echo "::add-matcher::.github/actionlint-matcher.json" + + set +e + actionlint -shellcheck= -pyflakes= 2>&1 | tee reports/actionlint.txt + status=${PIPESTATUS[0]} + + echo "::remove-matcher owner=actionlint::" + actionlint \ + -shellcheck= \ + -pyflakes= \ + -format "$(cat .github/actionlint-sarif-template.txt)" \ + > reports/actionlint.sarif + sarif_status=$? + set -e + + if [ "$status" -le 1 ] && [ "$sarif_status" -ne "$status" ]; then + echo "::error::Actionlint could not produce SARIF (exit $sarif_status)." + exit "$sarif_status" + fi + + { + echo "### Actionlint" + echo + } >> "$GITHUB_STEP_SUMMARY" + + case "$status" in + 0) + echo "No findings." >> "$GITHUB_STEP_SUMMARY" + ;; + 1) + echo "::warning::Actionlint reported findings; this check is informational." + echo "Findings were reported as annotations and do not fail CI." >> "$GITHUB_STEP_SUMMARY" + ;; + 2|3) + echo "::error::Actionlint could not complete (exit $status)." + echo "Actionlint failed to run correctly (exit $status)." >> "$GITHUB_STEP_SUMMARY" + exit "$status" + ;; + *) + echo "::error::Actionlint returned unexpected exit code $status." + echo "Actionlint returned unexpected exit code $status." >> "$GITHUB_STEP_SUMMARY" + exit "$status" + ;; + esac + + - name: Upload Actionlint SARIF to Code Scanning + if: success() + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + sarif_file: reports/actionlint.sarif + category: actionlint + + - name: Upload Actionlint report + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: actionlint-${{ github.run_id }} + path: | + reports/actionlint.txt + reports/actionlint.sarif + if-no-files-found: ignore + retention-days: 14 + + zizmor: + name: Zizmor High report (informational) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Nix + uses: ./.github/actions/setup-nix + with: + cachix-auth-token: ${{ github.event_name != 'pull_request' && secrets.CACHIX_AUTH_TOKEN || '' }} + + - name: Run Zizmor + run: | + set -euo pipefail + mkdir -p reports + + zizmor \ + --offline \ + --persona=regular \ + --min-severity=high \ + --no-exit-codes \ + --format=json \ + . > reports/zizmor-high.json + + zizmor \ + --offline \ + --persona=regular \ + --min-severity=high \ + --no-exit-codes \ + --format=sarif \ + . > reports/zizmor-high.sarif + + finding_count=$(jq 'length' reports/zizmor-high.json) + { + echo "### Zizmor high-severity report" + echo + echo "Zizmor has no critical severity; high is its maximum level." + echo + echo "Findings: $finding_count" + if [ "$finding_count" -gt 0 ]; then + echo + jq -r \ + 'group_by(.ident) | .[] | "- `\(.[0].ident)`: \(length)"' \ + reports/zizmor-high.json + echo + echo "These findings are informational and do not fail CI." + fi + } >> "$GITHUB_STEP_SUMMARY" + + if [ "$finding_count" -gt 0 ]; then + echo "::warning::Zizmor reported $finding_count high-severity findings; this check is informational." + fi + + - name: Upload Zizmor SARIF to Code Scanning + if: success() + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + sarif_file: reports/zizmor-high.sarif + category: zizmor-high + + - name: Upload Zizmor reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: zizmor-high-${{ github.run_id }} + path: | + reports/zizmor-high.json + reports/zizmor-high.sarif + if-no-files-found: ignore + retention-days: 14 diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000000..7b1e035b77 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +rules: + dangerous-triggers: + ignore: + # These base-branch workflows never check out or execute pull request + # head code. Keep each suppression scoped to its reviewed trigger block. + - dco.yml:3 + - e2e-label-help.yml:13 + - release-canary.yml:3 + - required-ci-gates.yml:3 + - vouch-check.yml:3 diff --git a/CI.md b/CI.md index 9dd919d1bd..c3d33622f7 100644 --- a/CI.md +++ b/CI.md @@ -26,6 +26,41 @@ The `OpenShell / E2E` and `OpenShell / GPU E2E` required statuses are evaluated The GitHub ruleset should require the `OpenShell / ...` statuses published by `Required CI Gates`, not the push-triggered workflow jobs directly. +## Informational security reports + +Security analysis that does not need NVIDIA infrastructure runs directly on +GitHub-hosted runners. These workflows receive no secrets and run on fork pull +requests without waiting for copy-pr-bot. Scanner jobs request +`security-events: write` to publish SARIF to Code Scanning. GitHub permits +Code Scanning uploads from `pull_request` runs even when fork and Dependabot +contexts receive a read-only `GITHUB_TOKEN`, so each scanner uploads results +directly and also retains report artifacts: + +- `Workflow Security Reports` runs Actionlint and Zizmor. Actionlint reports + workflow syntax and expression findings. Zizmor reports only High severity, + its maximum level. Nix provides both scanners. They publish SARIF to Code + Scanning and retain report artifacts. +- `Dependency Review` compares the base and head dependency graphs and reports + newly introduced vulnerabilities with a High-or-higher policy. It runs in + warn-only mode. A preflight turns an unavailable GitHub Dependency Graph into + a warning, so the workflow remains neutral until the repository feature is + available. +- `CodeQL` analyzes product Rust code, examples, and the Go, Python, and + TypeScript SDKs. E2E test code is excluded. Results are uploaded to Code + Scanning and always retained as workflow artifacts. + +Findings do not fail these workflows. Tool startup, configuration, build, and +analysis failures still fail so a broken scanner cannot appear healthy. These +workflows also run on merge groups, but their checks are not required statuses +and do not gate merges. + +Run the workflow-definition scanners locally with: + +```shell +nix develop --command actionlint -shellcheck= -pyflakes= +nix develop --command zizmor --offline --persona=regular --min-severity=high --no-exit-codes . +``` + ## Commit signing copy-pr-bot decides whether to mirror a PR automatically based on whether the author is trusted. For org members and collaborators, "trusted" means **all commits in the PR are cryptographically signed**. Unsigned commits, even from an org member, force the bot to wait for a maintainer's `/ok to test `. @@ -138,10 +173,14 @@ The bot's full administrator documentation is internal to NVIDIA. The only comma | `.github/workflows/branch-checks.yml` | Required non-E2E checks. Triggers on `push: pull-request/[0-9]+` for PR mirrors and `merge_group` for queued merges. | | `.github/workflows/branch-e2e.yml` | Standard, GPU, Kubernetes HA, and Kubernetes credential-driver E2E. PR mirror pushes use `test:e2e`, `test:e2e-gpu`, and `test:e2e-kubernetes` labels; merge groups run core and GPU E2E. | | `.github/workflows/helm-lint.yml` | Helm chart validation. PR mirror pushes skip lint jobs unless Helm inputs changed; merge groups always validate Helm because they represent the final integration state. | +| `.github/actions/setup-nix/action.yml` | Installs Nix and configures the OpenShell Cachix cache, using read-only cache access when no authentication token is available. | | `.github/actions/pr-gate/action.yml` | Composite action that resolves PR metadata and verifies the required label is set for PR mirror pushes. Non-push events are allowed through. | | `.github/actions/pr-merge-base/action.yml` | Composite action that resolves and fetches the merge-base commit for `pull-request/` push workflows. | | `.github/workflows/required-ci-gates.yml` | Posts required PR-head and merge-group statuses for gated CI workflows. This is what branch protection and merge queue should require. | | `.github/workflows/e2e-label-help.yml` | When a `test:e2e*` label is applied, posts a PR comment telling the maintainer the next manual step (re-run an existing workflow run, or `/ok to test ` to refresh the mirror). | +| `.github/workflows/workflow-security.yml` | Runs informational Actionlint and High-severity Zizmor reports on GitHub-hosted runners. | +| `.github/workflows/dependency-review.yml` | Reports dependency changes when GitHub Dependency Graph is available; otherwise publishes a neutral warning. | +| `.github/workflows/codeql.yml` | Runs informational CodeQL analysis for Rust and the Go, Python, and TypeScript SDKs and retains SARIF artifacts. | ## Release workflows @@ -163,3 +202,6 @@ Require these statuses in the branch ruleset for PR and merge-queue CI: - `OpenShell / Helm Lint` Do not require the underlying workflow jobs directly. PR workflow jobs only appear after copy-pr-bot mirrors trusted code, and merge-group workflow jobs run on temporary queue branches. The stable `OpenShell / ...` contexts prove the expected workflow completed for the commit that GitHub is about to merge. + +Do not add the informational Actionlint, Zizmor, Dependency Review, or CodeQL +jobs to the required status list while they remain in observation mode. diff --git a/architecture/build.md b/architecture/build.md index 7daf4a7c66..a1c6974989 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -272,6 +272,45 @@ Repository CI keeps telemetry compiled into release-parity artifacts but disables emission for Rust tests, E2E runs, and release canaries. This prevents synthetic activity from contributing to product usage metrics. +Static security checks are deliberately outside the mirror-branch path. They run +directly on GitHub-hosted runners with no secrets, so they also cover fork pull +requests and consume no NVIDIA self-hosted capacity. Scanner jobs request +`security-events: write` and upload SARIF to Code Scanning directly on every +event they run on, including fork and Dependabot pull requests, which Code +Scanning permits for `pull_request` runs despite their read-only `GITHUB_TOKEN`. +Each scanner also retains its report as a workflow artifact. No privileged +intermediate workflow relays those uploads. +Triggers differ by workflow: `.github/workflows/workflow-security.yml` and +`.github/workflows/codeql.yml` run on `pull_request`, `merge_group`, `main`, and +a weekly schedule; `.github/workflows/dependency-review.yml` runs on +`pull_request` and `merge_group` only, because it needs a base and head commit +to compare. + +- **Actionlint and Zizmor** analyze the workflow definitions themselves. + Repository configuration lives in `.github/actionlint.yml` (self-hosted runner + labels, scoped per-file ignores) and `.github/zizmor.yml` (scoped rule + suppressions). Zizmor runs offline and reports only High severity, which is + its maximum level. Both publish SARIF to Code Scanning and retain report + artifacts. The Nix flake provides both scanners, so local runs use + `nix develop --command actionlint -shellcheck= -pyflakes=` and + `nix develop --command zizmor --offline --persona=regular --min-severity=high --no-exit-codes .`. +- **Dependency Review** compares the base and head dependency graphs. It + preflights the GitHub Dependency Graph compare API and neutralizes itself with + a warning while that repository feature is unavailable, so the check begins + reporting on its own once the feature is enabled. Reviews run in warn-only + mode. +- **CodeQL** analyzes product Rust code, examples, and the Go, Python, and + TypeScript SDKs, scoped by `.github/codeql/codeql-config.yml`; E2E test code is + excluded. Only Go requires a build; the other languages use build mode `none`. + Results are uploaded to Code Scanning and always retained as workflow + artifacts. + +Findings never fail these checks; scanner and build failures do. A scanner that +cannot run, a CodeQL analyzer that does not complete, and an unexpected +Dependency Graph API error are all errors, which keeps an informational check +from silently degrading into a no-op. None of these checks are required +statuses, so they do not gate merges. + See `CI.md` for the contributor workflow, labels, and maintainer merge-queue workflow. ## Docs Site diff --git a/flake.nix b/flake.nix index 8e9fb728b1..68e6156e3b 100644 --- a/flake.nix +++ b/flake.nix @@ -46,6 +46,7 @@ }; testGuestPkgs = import nixpkgs-test-guest { inherit system; }; commonDevShellPackages = with pkgs; [ + actionlint cargo-deny cargo-nextest # Assemble Debian artifacts on macOS and Linux. @@ -55,6 +56,7 @@ # Coverage. lcov uv + zizmor ]; treefmtEval = treefmt-nix.lib.evalModule pkgs { projectRootFile = "flake.nix";