# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Security Workflows — security suite reusable workflow. # # Fans out to the published scanner workflows. Jobs deliberately have no dependencies # on each other so GitHub Actions schedules the enabled scans in parallel. # # Every scan is opt-in: a caller enables exactly the scans that apply to its repository, # because no single scan set fits every repository (runner access, CodeQL Default setup, # and language mix all differ). # # CONVENTION FOR FUTURE SCANS: add a new scan here with `enable-: false`. Flipping a # default from false to true silently changes what every existing caller runs, so it is a # MAJOR version bump — see the Versioning section of ../../README.md. # # PERMISSIONS: a caller must grant the union of every scan job's permissions below, including # scans it disables. GitHub validates the caller against the permissions this file declares # before evaluating any `if:`, so a short grant is a startup failure, not a skipped job. Adding # a scan that needs a scope no existing scan declares is therefore also a MAJOR version bump. name: Security Suite on: workflow_call: inputs: enable-secret-scan: description: >- Run the Pulse secret scan (secret-scan-pulse.yml). Off by default. Requires self-hosted nv-gha-runners. type: boolean required: false default: false enable-vuln-scan: description: >- Run the Pulse OSS vulnerability scan (vuln-scan-pulse-oss.yml). Off by default. Requires self-hosted nv-gha-runners. Set nspect-id to record findings against an nSpect registration; without it the scan runs unregistered and rapid-only. type: boolean required: false default: false enable-malware-scan: description: >- Run the Pulse malware scan (malware-scan-pulse.yml). Off by default. Requires self-hosted nv-gha-runners and an nSpect registration — set nspect-id when you do. type: boolean required: false default: false enable-sast-scan: description: >- Run the CodeQL SAST scan (sast-scan-codeql.yml). Off by default. Enable it only for repositories deliberately on Advanced setup, and set sast-languages when you do. type: boolean required: false default: false suite-runs-on: description: >- Runner for the suite's short helper jobs — the preflight check and the CodeQL language-matrix resolver. Set this only if the repository cannot use GitHub-hosted runners. type: string required: false default: ubuntu-latest nspect-id: description: >- nSpect ID for the calling repository (NSPECT-xxxx-xxxx), normally passed from the vars.NSPECT_ID repository variable. Carries no scan prefix because an nSpect ID belongs to the repository rather than to one scan — every scan here that reports to nSpect reads this one input. Optional for enable-vuln-scan: left empty, the vulnerability scan runs unregistered and rapid-only. Required for enable-malware-scan. type: string required: false default: '' secret-runs-on: description: Runner label for the Pulse secret scan (an nv-gha-runners label). type: string required: false default: linux-amd64-cpu4 secret-failure-policy: description: >- Pulse finding policy: unverified (default), strict, or all. See secret-scan-pulse.yml for the full policy semantics. type: string required: false default: unverified vuln-registered-branch: description: Default or release branch registered in nSpect. Never a pull-request/* mirror. type: string required: false default: main vuln-sbom-name: description: >- SBOM name of the nSpect registration to scan against, for a registered branch with more than one SBOM entry. Needs nspect-id; required with vuln-inventory-writeback. See vuln-scan-pulse-oss.yml (sbom_name). type: string required: false default: '' vuln-runs-on: description: Runner label for the Pulse OSS vulnerability scan (an nv-gha-runners label). type: string required: false default: linux-amd64-cpu4 vuln-scan-path: description: Repository-relative directory to scan. type: string required: false default: . vuln-scan-mode: description: >- Scan family to run: auto (default), persistent, or rapid. See vuln-scan-pulse-oss.yml for the full mode semantics. type: string required: false default: auto vuln-build: description: >- Enable scanner build mode, for ecosystems whose dependency graph is only resolvable through a build (Maven, Go). type: boolean required: false default: false vuln-failure-policy: description: >- Pulse OSS finding policy: report-only (default) or fail-on-vulnerability. Scanner and transport errors fail the job under either value. type: string required: false default: report-only vuln-detect-properties: description: JSON object of supported Detect tuning properties. See docs/vuln-scan. type: string required: false default: '{}' vuln-show-console: description: Print scanner output with credentials and internal destinations removed. type: boolean required: false default: false vuln-debug-logging: description: Enable Pulse debug logging. Required redaction still applies. type: boolean required: false default: false vuln-python-wheels-artifact: description: Current-run artifact of prebuilt Python runtime wheels. Requires vuln-python-project. type: string required: false default: '' vuln-python-project: description: Installed Python distribution to inspect from the supplied runtime wheels. type: string required: false default: '' vuln-inventory-writeback: description: Publish results to nSpect inventory (persistent scans only). Requires vuln-sbom-name. type: boolean required: false default: false vuln-upload-report: description: Upload the validated vulnerability report as a 3-day artifact. type: boolean required: false default: false vuln-timeout-seconds: description: Scanner timeout in seconds (60-3600). type: number required: false default: 1800 malware-runs-on: description: Runner label for the Pulse malware scan (an nv-gha-runners label). type: string required: false default: linux-amd64-cpu4 malware-failure-policy: description: >- Pulse malware verdict policy: strict (default) or malicious-only. A MALICIOUS verdict, and any scan that ends without a verdict, fail the job under either value. type: string required: false default: strict malware-upload-report: description: Upload the validated malware report, nSpect ID redacted, as a 3-day artifact. type: boolean required: false default: false malware-timeout-minutes: description: >- Job timeout for the Pulse malware scan, in minutes (10 to 360). The scan's report polling is derived from it. type: number required: false default: 60 sast-languages: description: >- JSON array of CodeQL languages, for example '["actions","python"]'. Entries may also be objects carrying per-language overrides: '[{"language":"go","build-mode":"autobuild"},"rust"]'. Accepted values: actions, c-cpp, csharp, go, java-kotlin, javascript-typescript, python, ruby, rust, swift. Each language's build mode is resolved automatically, so languages with incompatible requirements can be combined — see sast-scan-codeql.yml for per-language caveats. Required when enable-sast-scan is true; ignored otherwise. type: string required: false default: '' sast-runs-on: description: >- Default runner label for the CodeQL analysis. Override per language with the object form of sast-languages. type: string required: false default: ubuntu-latest sast-build-mode: description: >- Preferred CodeQL build mode: none (default) or autobuild. Applied to every language that supports it; a language that cannot honour it resolves to the mode CodeQL does support and reports the substitution. type: string required: false default: none sast-queries: description: CodeQL query suite(s), comma-separated. type: string required: false default: security-extended sast-config-file: description: Optional path in the consuming repository to a CodeQL config file. type: string required: false default: '' permissions: contents: read jobs: preflight: name: Suite preflight if: >- ${{ (!inputs.enable-secret-scan && !inputs.enable-vuln-scan && !inputs.enable-malware-scan && !inputs.enable-sast-scan) || (inputs.enable-malware-scan && inputs.nspect-id == '') || (inputs.enable-sast-scan && inputs.sast-languages == '') }} runs-on: ${{ inputs.suite-runs-on }} steps: - name: Report suite selection env: ENABLE_SECRET_SCAN: ${{ inputs.enable-secret-scan }} ENABLE_VULN_SCAN: ${{ inputs.enable-vuln-scan }} ENABLE_MALWARE_SCAN: ${{ inputs.enable-malware-scan }} ENABLE_SAST_SCAN: ${{ inputs.enable-sast-scan }} NSPECT_ID: ${{ inputs.nspect-id }} SAST_LANGUAGES: ${{ inputs.sast-languages }} run: | set -euo pipefail # Report every missing companion input before exiting, so a caller enabling two # scans at once does not have to fix one, re-run, and discover the next. failures=0 if [ "$ENABLE_MALWARE_SCAN" = "true" ] && [ -z "$NSPECT_ID" ]; then echo "::error::enable-malware-scan is true but nspect-id is empty. Pass the repository's nSpect ID from the vars.NSPECT_ID repository variable." failures=$((failures + 1)) fi if [ "$ENABLE_SAST_SCAN" = "true" ] && [ -z "$SAST_LANGUAGES" ]; then echo "::error::enable-sast-scan is true but sast-languages is empty. Pass a JSON array of CodeQL languages, for example: sast-languages: '[\"actions\"]'." failures=$((failures + 1)) fi if [ "$failures" -ne 0 ]; then exit 1 fi if [ "$ENABLE_SECRET_SCAN" != "true" ] && [ "$ENABLE_VULN_SCAN" != "true" ] && [ "$ENABLE_MALWARE_SCAN" != "true" ] && [ "$ENABLE_SAST_SCAN" != "true" ]; then echo "::warning::Security suite ran no scans — every enable-* input is false. Set enable-secret-scan, enable-vuln-scan, enable-malware-scan and/or enable-sast-scan to true for this check to mean anything." fi secret-scan: name: Secret scan (Pulse) if: ${{ inputs.enable-secret-scan }} permissions: actions: read contents: read id-token: write security-events: write uses: ./.github/workflows/secret-scan-pulse.yml with: runs-on: ${{ inputs.secret-runs-on }} failure_policy: ${{ inputs.secret-failure-policy }} vuln-scan: name: Vulnerability scan (Pulse OSS) if: ${{ inputs.enable-vuln-scan }} permissions: contents: read id-token: write uses: ./.github/workflows/vuln-scan-pulse-oss.yml with: runs-on: ${{ inputs.vuln-runs-on }} nspect_id: ${{ inputs.nspect-id }} registered_branch: ${{ inputs.vuln-registered-branch }} sbom_name: ${{ inputs.vuln-sbom-name }} scan_path: ${{ inputs.vuln-scan-path }} scan_mode: ${{ inputs.vuln-scan-mode }} build: ${{ inputs.vuln-build }} python_wheels_artifact: ${{ inputs.vuln-python-wheels-artifact }} python_project: ${{ inputs.vuln-python-project }} failure_policy: ${{ inputs.vuln-failure-policy }} detect_properties: ${{ inputs.vuln-detect-properties }} show_console: ${{ inputs.vuln-show-console }} debug_logging: ${{ inputs.vuln-debug-logging }} inventory_writeback: ${{ inputs.vuln-inventory-writeback }} upload_report: ${{ inputs.vuln-upload-report }} timeout_seconds: ${{ inputs.vuln-timeout-seconds }} malware-scan: name: Malware scan (Pulse) if: ${{ inputs.enable-malware-scan && inputs.nspect-id != '' }} permissions: id-token: write uses: ./.github/workflows/malware-scan-pulse.yml with: runs-on: ${{ inputs.malware-runs-on }} nspect_id: ${{ inputs.nspect-id }} failure_policy: ${{ inputs.malware-failure-policy }} upload_report: ${{ inputs.malware-upload-report }} timeout_minutes: ${{ inputs.malware-timeout-minutes }} sast-scan: name: SAST (CodeQL) if: ${{ inputs.enable-sast-scan && inputs.sast-languages != '' }} permissions: actions: read contents: read security-events: write uses: ./.github/workflows/sast-scan-codeql.yml with: languages: ${{ inputs.sast-languages }} runs-on: ${{ inputs.sast-runs-on }} build-mode: ${{ inputs.sast-build-mode }} resolve-runs-on: ${{ inputs.suite-runs-on }} queries: ${{ inputs.sast-queries }} config-file: ${{ inputs.sast-config-file }}