From 8e2b40d30d288074c6d621d8fbe332f3aa8dda44 Mon Sep 17 00:00:00 2001 From: Adrien Langou Date: Mon, 24 Aug 2026 11:34:19 +0200 Subject: [PATCH 1/2] ci(security): add informational security checks Signed-off-by: Adrien Langou --- .github/actionlint-matcher.json | 17 ++ .github/actionlint-sarif-template.txt | 63 ++++++ .github/actionlint.yml | 18 ++ .github/codeql/codeql-config.yml | 15 ++ .github/workflows/codeql.yml | 131 +++++++++++++ .github/workflows/dependency-review.yml | 95 +++++++++ .github/workflows/security-report-upload.yml | 93 +++++++++ .github/workflows/workflow-security.yml | 193 +++++++++++++++++++ .github/zizmor.yml | 14 ++ CI.md | 42 ++++ architecture/build.md | 37 ++++ mise.lock | 56 ++++++ mise.toml | 2 + tasks/security.toml | 12 ++ 14 files changed, 788 insertions(+) create mode 100644 .github/actionlint-matcher.json create mode 100644 .github/actionlint-sarif-template.txt create mode 100644 .github/actionlint.yml create mode 100644 .github/codeql/codeql-config.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/security-report-upload.yml create mode 100644 .github/workflows/workflow-security.yml create mode 100644 .github/zizmor.yml create mode 100644 tasks/security.toml 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/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..81d318dd4a --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,131 @@ +# 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' && + (github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository) + 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..a86732c147 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,95 @@ +# 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 + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + sha_pattern='^([0-9a-fA-F]{40}|[0-9a-fA-F]{64})$' + if [[ ! "$BASE_REF" =~ $sha_pattern || ! "$HEAD_REF" =~ $sha_pattern ]]; then + echo "::error::Dependency Review requires base and head commit SHAs." + exit 2 + fi + + response_file="$RUNNER_TEMP/dependency-review-preflight.json" + http_status=$( + curl \ + --silent \ + --show-error \ + --output "$response_file" \ + --write-out "%{http_code}" \ + --header "Accept: application/vnd.github+json" \ + --header "Authorization: Bearer $GH_TOKEN" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/dependency-graph/compare/$BASE_REF...$HEAD_REF" + ) + + case "$http_status" in + 200) + echo "available=true" >> "$GITHUB_OUTPUT" + ;; + 403|404) + echo "available=false" >> "$GITHUB_OUTPUT" + echo "::warning::GitHub Dependency Graph is unavailable (HTTP $http_status); Dependency Review is skipped." + { + echo "### Dependency Review" + echo + echo "GitHub Dependency Graph is unavailable (HTTP $http_status)." + echo "The informational review will start automatically once the repository feature is available." + } >> "$GITHUB_STEP_SUMMARY" + ;; + *) + message=$(jq -r '.message // "unknown API error"' "$response_file") + echo "::error::Dependency Graph preflight failed with HTTP $http_status: $message" + exit 1 + ;; + esac + + - 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/security-report-upload.yml b/.github/workflows/security-report-upload.yml new file mode 100644 index 0000000000..5daadbf64f --- /dev/null +++ b/.github/workflows/security-report-upload.yml @@ -0,0 +1,93 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Upload Fork Security Reports + +on: + workflow_run: + workflows: ["CodeQL", "Workflow Security Reports"] + types: [completed] + +permissions: + actions: read + contents: read + security-events: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.workflow_run.id }} + cancel-in-progress: false + +jobs: + codeql: + name: Upload CodeQL (${{ matrix.language }}) + if: >- + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.name == 'CodeQL' && + github.event.workflow_run.head_repository.full_name != github.repository && + github.event.workflow_run.pull_requests[0].number != null + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + language: + - rust + - go + - python + - javascript-typescript + steps: + - name: Download CodeQL SARIF + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: codeql-${{ matrix.language }}-${{ github.event.workflow_run.id }} + path: reports + github-token: ${{ github.token }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Upload CodeQL SARIF + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + sarif_file: reports + category: /language:${{ matrix.language }} + ref: refs/pull/${{ github.event.workflow_run.pull_requests[0].number }}/head + sha: ${{ github.event.workflow_run.head_sha }} + + workflow-security: + name: Upload ${{ matrix.name }} + if: >- + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.name == 'Workflow Security Reports' && + github.event.workflow_run.head_repository.full_name != github.repository && + github.event.workflow_run.pull_requests[0].number != null + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + include: + - name: Actionlint + artifact: actionlint + sarif: actionlint.sarif + category: actionlint + - name: Zizmor + artifact: zizmor-high + sarif: zizmor-high.sarif + category: zizmor-high + steps: + - name: Download ${{ matrix.name }} SARIF + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ matrix.artifact }}-${{ github.event.workflow_run.id }} + path: reports + github-token: ${{ github.token }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Upload ${{ matrix.name }} SARIF + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + sarif_file: reports/${{ matrix.sarif }} + category: ${{ matrix.category }} + ref: refs/pull/${{ github.event.workflow_run.pull_requests[0].number }}/head + sha: ${{ github.event.workflow_run.head_sha }} diff --git a/.github/workflows/workflow-security.yml b/.github/workflows/workflow-security.yml new file mode 100644 index 0000000000..b4345e6262 --- /dev/null +++ b/.github/workflows/workflow-security.yml @@ -0,0 +1,193 @@ +# 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 + +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: Install Actionlint + uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 + with: + version: 2026.4.25 + experimental: true + install_args: github:rhysd/actionlint + + - name: Run Actionlint + shell: bash + 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() && + (github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository) + 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: Install Zizmor + uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 + with: + version: 2026.4.25 + experimental: true + install_args: github:zizmorcore/zizmor + + - name: Run Zizmor + shell: bash + 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() && + (github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository) + 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..65407d2812 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,14 @@ +# 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 + - security-report-upload.yml:6 + - vouch-check.yml:3 diff --git a/CI.md b/CI.md index aae22f4c4f..e9db3fb4bb 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 on trusted repository contexts. Fork +pull requests retain SARIF as artifacts in the unprivileged scanner run; the +privileged `Upload Fork Security Reports` workflow ingests those artifacts +without checking out or executing fork code: + +- `Workflow Security Reports` runs Actionlint and Zizmor. Actionlint reports + workflow syntax and expression findings. Zizmor reports only High severity, + its maximum level. Both 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 +mise run security:actionlint +mise run security:zizmor +``` + ## 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 `. @@ -142,6 +177,10 @@ The bot's full administrator documentation is internal to NVIDIA. The only comma | `.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/security-report-upload.yml` | Uploads SARIF artifacts from successful, unprivileged fork PR scans without checking out or executing fork code. | +| `.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..8b072c07a1 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -272,6 +272,43 @@ 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` to publish SARIF on trusted repository contexts. Fork +pull requests retain SARIF in their unprivileged scanner runs; a separate +`workflow_run` workflow ingests those artifacts with write permission without +checking out or executing fork code. +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 same commands are available locally through `tasks/security.toml` + as `mise run security:actionlint` and `mise run security:zizmor`. +- **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/mise.lock b/mise.lock index 23ce8b24e0..cd8a77eef1 100644 --- a/mise.lock +++ b/mise.lock @@ -134,6 +134,34 @@ checksum = "sha256:b8514ed7552e148b0a032114f745118dcb801791adafafeaf9935e4bfb0ed url = "https://github.com/mozilla/sccache/releases/download/v0.16.0/sccache-v0.16.0-x86_64-pc-windows-msvc.zip" url_api = "https://api.github.com/repos/mozilla/sccache/releases/assets/452060720" +[[tools."github:rhysd/actionlint"]] +version = "1.7.12" +backend = "github:rhysd/actionlint" + +[tools."github:rhysd/actionlint"."platforms.linux-arm64"] +checksum = "sha256:325e971b6ba9bfa504672e29be93c24981eeb1c07576d730e9f7c8805afff0c6" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924897" +provenance = "github-attestations" + +[tools."github:rhysd/actionlint"."platforms.linux-x64"] +checksum = "sha256:8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924896" +provenance = "github-attestations" + +[tools."github:rhysd/actionlint"."platforms.macos-arm64"] +checksum = "sha256:aba9ced2dee8d27fecca3dc7feb1a7f9a52caefa1eb46f3271ea66b6e0e6953f" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_darwin_arm64.tar.gz" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924893" +provenance = "github-attestations" + +[tools."github:rhysd/actionlint"."platforms.windows-x64"] +checksum = "sha256:6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f6e9" +url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_windows_amd64.zip" +url_api = "https://api.github.com/repos/rhysd/actionlint/releases/assets/384924919" +provenance = "github-attestations" + [[tools."github:rust-cross/cargo-zigbuild"]] version = "0.22.3" backend = "github:rust-cross/cargo-zigbuild" @@ -182,6 +210,34 @@ checksum = "sha256:83a7d5955c7ac96ede5d896ac9ede5f7ecce9ece0e95d9e47acd766b09e2e url = "https://github.com/rust-secure-code/cargo-auditable/releases/download/v0.7.5/cargo-auditable-x86_64-pc-windows-msvc.zip" url_api = "https://api.github.com/repos/rust-secure-code/cargo-auditable/releases/assets/426566657" +[[tools."github:zizmorcore/zizmor"]] +version = "1.29.0" +backend = "github:zizmorcore/zizmor" + +[tools."github:zizmorcore/zizmor"."platforms.linux-arm64"] +checksum = "sha256:415eaa7c0a06479a701b8e44a3e812c1047decc848ec4bede7bd6bbf49f22d20" +url = "https://github.com/zizmorcore/zizmor/releases/download/v1.29.0/zizmor-aarch64-unknown-linux-gnu.tar.gz" +url_api = "https://api.github.com/repos/zizmorcore/zizmor/releases/assets/498263143" +provenance = "github-attestations" + +[tools."github:zizmorcore/zizmor"."platforms.linux-x64"] +checksum = "sha256:dd96df044a6e8538d5f423790f453bdd03d49e5b2bcc38214acc41a2f1297839" +url = "https://github.com/zizmorcore/zizmor/releases/download/v1.29.0/zizmor-x86_64-unknown-linux-gnu.tar.gz" +url_api = "https://api.github.com/repos/zizmorcore/zizmor/releases/assets/498263145" +provenance = "github-attestations" + +[tools."github:zizmorcore/zizmor"."platforms.macos-arm64"] +checksum = "sha256:720322fade9e83a9c7953944c438f2ba942636b86b96a8f0e6b15ce94c8a6b6f" +url = "https://github.com/zizmorcore/zizmor/releases/download/v1.29.0/zizmor-aarch64-apple-darwin.tar.gz" +url_api = "https://api.github.com/repos/zizmorcore/zizmor/releases/assets/498263141" +provenance = "github-attestations" + +[tools."github:zizmorcore/zizmor"."platforms.windows-x64"] +checksum = "sha256:68a6bc6888f10bf0d53658c75885e7c1b7a0588d4c1fbc3f0ca280ad7324bf06" +url = "https://github.com/zizmorcore/zizmor/releases/download/v1.29.0/zizmor-x86_64-pc-windows-msvc.zip" +url_api = "https://api.github.com/repos/zizmorcore/zizmor/releases/assets/498263142" +provenance = "github-attestations" + [[tools.go]] version = "1.26.5" backend = "core:go" diff --git a/mise.toml b/mise.toml index ec643ed08e..810edf040d 100644 --- a/mise.toml +++ b/mise.toml @@ -41,6 +41,8 @@ k3d = { version = "5.8.3", os = ["macos"] } "github:anchore/syft" = { version = "1.44.0" } "github:EmbarkStudios/cargo-about" = { version = "0.8.4", version_prefix = "" } "github:EmbarkStudios/cargo-deny" = { version = "0.20.2", version_prefix = "" } +"github:rhysd/actionlint" = "1.7.12" +"github:zizmorcore/zizmor" = "1.29.0" zig = "0.14.1" "github:rust-secure-code/cargo-auditable" = "0.7.5" "github:rust-cross/cargo-zigbuild" = "0.22.3" diff --git a/tasks/security.toml b/tasks/security.toml new file mode 100644 index 0000000000..5287db9a02 --- /dev/null +++ b/tasks/security.toml @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Repository and workflow security checks + +["security:actionlint"] +description = "Lint GitHub Actions workflow syntax and expressions" +run = "actionlint -shellcheck= -pyflakes=" + +["security:zizmor"] +description = "Report high-severity GitHub Actions security findings" +run = "zizmor --offline --persona=regular --min-severity=high --no-exit-codes ." From 4a321797503c7c7bb9d7d584545add82dc1692ff Mon Sep 17 00:00:00 2001 From: Adrien Langou Date: Wed, 26 Aug 2026 15:27:42 +0200 Subject: [PATCH 2/2] ci(dependabot): broaden grouped dependency updates Cover maintained package ecosystems on a weekly cadence while grouping updates and limiting version-update PR churn. Signed-off-by: Adrien Langou --- .github/dependabot.yml | 123 ++++++++++++++++++++++++++++++++++++++++- CI.md | 6 ++ architecture/build.md | 6 ++ 3 files changed, 133 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 29aeab9aa1..c65b8667bb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,6 +3,125 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "Etc/UTC" cooldown: - default-days: 2 + default-days: 7 + open-pull-requests-limit: 2 + groups: + github-actions-version-updates: + applies-to: "version-updates" + patterns: + - "*" + github-actions-security-updates: + applies-to: "security-updates" + patterns: + - "*" + + - package-ecosystem: "cargo" + directories: + - "/" + - "/e2e/rust" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "Etc/UTC" + cooldown: + default-days: 7 + open-pull-requests-limit: 2 + groups: + rust-version-updates: + group-by: "dependency-name" + rust-security-updates: + applies-to: "security-updates" + patterns: + - "*" + + - package-ecosystem: "gomod" + directory: "/sdk/go" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "Etc/UTC" + cooldown: + default-days: 7 + open-pull-requests-limit: 2 + groups: + go-version-updates: + applies-to: "version-updates" + patterns: + - "*" + update-types: + - "minor" + - "patch" + go-security-updates: + applies-to: "security-updates" + patterns: + - "*" + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "Etc/UTC" + cooldown: + default-days: 7 + open-pull-requests-limit: 2 + groups: + python-version-updates: + applies-to: "version-updates" + patterns: + - "*" + update-types: + - "minor" + - "patch" + python-security-updates: + applies-to: "security-updates" + patterns: + - "*" + + - package-ecosystem: "npm" + directories: + - "/sdk/typescript" + - "/scripts/lint-mermaid" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "Etc/UTC" + cooldown: + default-days: 7 + open-pull-requests-limit: 2 + groups: + npm-version-updates: + group-by: "dependency-name" + npm-security-updates: + applies-to: "security-updates" + patterns: + - "*" + + - package-ecosystem: "docker" + directories: + - "/deploy/docker" + - "/scripts/agents/gator" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "Etc/UTC" + cooldown: + default-days: 7 + open-pull-requests-limit: 2 + groups: + docker-version-updates: + group-by: "dependency-name" + docker-security-updates: + applies-to: "security-updates" + patterns: + - "*" diff --git a/CI.md b/CI.md index e9db3fb4bb..c0b2c5dc2d 100644 --- a/CI.md +++ b/CI.md @@ -45,6 +45,12 @@ without checking out or executing fork code: 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. +- Dependabot checks GitHub Actions, the Rust workspaces, the Go and Python SDKs, + the TypeScript and documentation npm projects, and production Dockerfiles + weekly. Multi-directory ecosystems group matching dependency updates, and + each ecosystem allows at most two open version-update pull requests. Security + updates use separate groups and depend on the repository's Dependabot and + auto-triage settings. - `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. diff --git a/architecture/build.md b/architecture/build.md index 8b072c07a1..a77f7b6bc9 100644 --- a/architecture/build.md +++ b/architecture/build.md @@ -297,6 +297,12 @@ to compare. 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. +- **Dependabot** checks GitHub Actions, Cargo, Go modules, Python, npm, and + production Dockerfiles weekly. Multi-directory ecosystems group updates by + dependency name; other ecosystems group compatible minor and patch updates. + Each ecosystem permits at most two open version-update pull requests, while + security updates use separate groups controlled by repository Dependabot and + auto-triage settings. - **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`.