diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..0a9e3923 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: + + # Check for updates to GitHub Actions + - package-ecosystem: "github-actions" + directories: + - "/" + - "/.github/actions/*" + schedule: + interval: "weekly" + groups: + github-actions: + patterns: + - "*" + cooldown: + default-days: 7 diff --git a/.github/workflows/_security_scan.yml b/.github/workflows/_security_scan.yml new file mode 100644 index 00000000..0790a697 --- /dev/null +++ b/.github/workflows/_security_scan.yml @@ -0,0 +1,178 @@ +# Copyright Advanced Micro Devices, Inc. +# SPDX-License-Identifier: MIT +# +# Shared engine for the security scanners (gitleaks / bandit / zizmor). +# +# The scanner scripts live in external tool repos (ROCm/TheRock, +# ROCm/rocm-tests). Each script imports `github_actions_api` from its own +# repo's build_tools/ and resolves a config file (gitleaks.toml / +# bandit.yaml / zizmor.yml) against the current working directory. None of +# that exists in this repo, so a plain `uses:` call to the upstream +# reusable workflow -- whose `run:` steps and `./.github/actions/...` refs +# resolve against THIS checkout -- fails on the missing files. +# +# Instead we check the tool repo out ourselves (into _tool), copy its +# config into the scan root, and run its script with the working directory +# set to the scan target. This workflow is fully self-contained (no +# caller-relative refs), so it can be lifted into a common ROCm security +# repo unchanged and called via `uses: ROCm//...@main`. +name: _security_scan + +on: + workflow_call: + inputs: + tool: + description: "Scanner name; drives the SARIF category and artifact name." + required: true + type: string + tool_repo: + description: "owner/repo holding the scanner script (checked out to _tool)." + required: true + type: string + tool_ref: + description: "Ref of tool_repo to check out." + required: false + type: string + default: main + script_path: + description: "Path to the scanner script inside the tool checkout." + required: true + type: string + config_path: + description: >- + Config file inside the tool checkout, copied to the scan root so + the scanner script can resolve it relative to its working + directory. + required: true + type: string + scan_mode: + description: "'changed' (default) or 'all'. See the scanner scripts." + required: false + type: string + default: changed + report_formats: + description: "Comma-separated report formats. See the scanner scripts." + required: false + type: string + default: sarif + scan_path: + description: "Path (relative to the scan root) to scan." + required: false + type: string + default: "." + severity_threshold: + description: "Minimum severity that fails the job (bandit / zizmor only)." + required: false + type: string + default: high + persona: + description: "Zizmor audit persona (zizmor only)." + required: false + type: string + default: regular + +# No permissions block here on purpose. A reusable workflow inherits the +# token its caller grants; declaring scopes here that a caller doesn't +# grant makes that caller fail at startup. So each caller is the single +# source of truth for its own scopes -- CSV-only callers grant just +# `contents: read`, SARIF callers additionally grant `security-events: +# write` (code-scanning API) and `actions: read` (private-repo +# workflow-run lookup that upload-sarif performs). + +jobs: + scan: + name: ${{ inputs.tool }} scan + runs-on: ubuntu-24.04 + timeout-minutes: 5 + steps: + # PR-aware checkout depth: PR commits + merge base lets the scanner + # walk base..head; full history (0) for everything else. GHA + # expressions can't do arithmetic, so compute it in bash. + - name: Compute fetch-depth + id: depth + env: + EVENT_NAME: ${{ github.event_name }} + PR_COMMITS: ${{ github.event.pull_request.commits }} + run: | + if [ "$EVENT_NAME" = "pull_request" ]; then + echo "value=$((PR_COMMITS + 1))" >> "$GITHUB_OUTPUT" + else + echo "value=0" >> "$GITHUB_OUTPUT" + fi + + # Scan target = the calling repo. In a reusable workflow an + # `actions/checkout` with no `repository:` defaults to + # github.repository, which is the caller. + - name: Checkout scan target + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: ${{ steps.depth.outputs.value }} + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + + - name: Checkout scanner tool + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: ${{ inputs.tool_repo }} + ref: ${{ inputs.tool_ref }} + path: _tool + fetch-depth: 1 + persist-credentials: false + + # The scanner resolves its config against the working directory, so + # stage the tool repo's config at the scan root. It stays untracked; + # history-based scans key off commits, not the working tree. + - name: Stage scanner config + env: + CONFIG_PATH: ${{ inputs.config_path }} + run: cp "_tool/${CONFIG_PATH}" ./ + + - name: Set up Python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: "3.12" + + # Each scanner reads only its own PREFIX_* vars, so exporting all + # three prefixes from the same inputs is a harmless no-op for the + # ones that don't apply. + - name: Run ${{ inputs.tool }} + id: scan + env: + GITLEAKS_SCAN_MODE: ${{ inputs.scan_mode }} + GITLEAKS_REPORT_FORMATS: ${{ inputs.report_formats }} + GITLEAKS_SOURCE_DIR: ${{ inputs.scan_path }} + BANDIT_SCAN_MODE: ${{ inputs.scan_mode }} + BANDIT_REPORT_FORMATS: ${{ inputs.report_formats }} + BANDIT_SOURCE_DIR: ${{ inputs.scan_path }} + BANDIT_SEVERITY_THRESHOLD: ${{ inputs.severity_threshold }} + ZIZMOR_SCAN_MODE: ${{ inputs.scan_mode }} + ZIZMOR_REPORT_FORMATS: ${{ inputs.report_formats }} + ZIZMOR_SOURCE_DIR: ${{ inputs.scan_path }} + ZIZMOR_SEVERITY_THRESHOLD: ${{ inputs.severity_threshold }} + ZIZMOR_PERSONA: ${{ inputs.persona }} + GH_TOKEN: ${{ github.token }} + SCRIPT_PATH: ${{ inputs.script_path }} + run: python "_tool/${SCRIPT_PATH}" + + # Fork PRs run with a read-only GITHUB_TOKEN regardless of the + # requested permissions, so the code-scanning upload would fail with + # "Resource not accessible by integration". Skip it for forks -- + # findings still fail the job (bandit/zizmor) and appear in the log. + - name: Upload SARIF report to code scanning + if: >- + always() + && steps.scan.outputs.sarif_path != '' + && (github.event_name != 'pull_request' + || github.event.pull_request.head.repo.full_name == github.repository) + uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + with: + sarif_file: ${{ steps.scan.outputs.sarif_path }} + category: ${{ inputs.tool }} + + - name: Upload non-SARIF reports + if: always() && steps.scan.outputs.non_sarif_paths != '' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ${{ inputs.tool }}-report + path: ${{ steps.scan.outputs.non_sarif_paths }} + if-no-files-found: ignore diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml new file mode 100644 index 00000000..af92db2b --- /dev/null +++ b/.github/workflows/bandit.yml @@ -0,0 +1,38 @@ +# Copyright Advanced Micro Devices, Inc. +# SPDX-License-Identifier: MIT +# +# Post-merge bandit scan: runs after every push to main/develop (typically +# the squash/rebase/merge of a PR) and pushes SARIF findings to the +# repository's code-scanning Security tab. +name: Bandit + +on: + push: + branches: [main, develop] + # Allow re-running the post-merge scan on demand, e.g. after tweaking + # `bandit.yaml` or rotating the bandit version pin, so the Security + # tab can be refreshed without an unrelated commit. + workflow_dispatch: + +permissions: + contents: read + +jobs: + bandit: + uses: ./.github/workflows/_security_scan.yml + with: + tool: bandit + tool_repo: ROCm/rocm-tests + script_path: scan_tools/github_actions/bandit.py + config_path: bandit.yaml + scan_mode: all + report_formats: sarif + permissions: + contents: read + # Required so the SARIF upload step can call + # github/codeql-action/upload-sarif and have the findings + # appear under Security -> Code scanning, filterable by + # `Tool: Bandit` separately from any other scanner. + security-events: write + # Private-repo requirement for upload-sarif (workflow-run lookup). + actions: read diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..03f6d95d --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,52 @@ +# Copyright Advanced Micro Devices, Inc. +# SPDX-License-Identifier: MIT +# +name: CodeQL + +on: + push: + branches: [main, develop] + paths: + - '**/*.py' + - '.github/**' + pull_request: + branches: [main, develop] + paths: + - '**/*.py' + - '.github/**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + analyze: + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-24.04 + timeout-minutes: 30 + permissions: + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: [python, actions] + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Initialize CodeQL + uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + with: + languages: ${{ matrix.language }} + queries: security-extended + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/gitleaks_pr.yml b/.github/workflows/gitleaks_pr.yml new file mode 100644 index 00000000..7544697e --- /dev/null +++ b/.github/workflows/gitleaks_pr.yml @@ -0,0 +1,28 @@ +# Copyright Advanced Micro Devices, Inc. +# SPDX-License-Identifier: MIT + +name: Gitleaks for PRs + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + # Scan commits introduced by this PR (`scan_mode: changed`). CSV report + # is uploaded as a build artifact and printed to the job summary so + # reviewers can browse findings directly from the PR's Checks tab. + gitleaks: + uses: ./.github/workflows/_security_scan.yml + with: + tool: gitleaks + tool_repo: ROCm/TheRock + script_path: build_tools/scan_tools/github_actions/gitleaks.py + config_path: gitleaks.toml + scan_mode: changed + report_formats: csv diff --git a/.github/workflows/gitleaks_scheduled.yml b/.github/workflows/gitleaks_scheduled.yml new file mode 100644 index 00000000..732dfc5b --- /dev/null +++ b/.github/workflows/gitleaks_scheduled.yml @@ -0,0 +1,38 @@ +# Copyright Advanced Micro Devices, Inc. +# SPDX-License-Identifier: MIT +# +# Weekly gitleaks scan: runs on a fixed cadence and pushes +# SARIF findings to the repository's code-scanning Security tab. +name: Gitleaks (Scheduled) + +on: + # Run every Saturday at 10:00 UTC. + schedule: + - cron: "0 10 * * 6" + # Allow re-running the weekly scan on demand, e.g. after + # tweaking `gitleaks.toml` or rotating the gitleaks version pin, so + # the Security tab can be refreshed without an unrelated commit. + workflow_dispatch: + +permissions: + contents: read + +jobs: + gitleaks: + uses: ./.github/workflows/_security_scan.yml + with: + tool: gitleaks + tool_repo: ROCm/TheRock + script_path: build_tools/scan_tools/github_actions/gitleaks.py + config_path: gitleaks.toml + scan_mode: all + report_formats: sarif + permissions: + contents: read + # Required so the SARIF upload step can call + # github/codeql-action/upload-sarif and have the findings + # appear under Security -> Code scanning, filterable by + # `Tool: gitleaks` separately from any other scanner. + security-events: write + # Private-repo requirement for upload-sarif (workflow-run lookup). + actions: read diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 00000000..72da4597 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,36 @@ +# Copyright Advanced Micro Devices, Inc. +# SPDX-License-Identifier: MIT + +name: Zizmor + +on: + pull_request: + push: + branches: [main, develop] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + zizmor: + uses: ./.github/workflows/_security_scan.yml + with: + tool: zizmor + tool_repo: ROCm/rocm-tests + script_path: scan_tools/github_actions/zizmor.py + config_path: zizmor.yml + scan_mode: all + report_formats: sarif + permissions: + contents: read + # Required so the SARIF upload step can call + # github/codeql-action/upload-sarif and have the findings + # appear under Security -> Code scanning, filterable by + # `Tool: zizmor` separately from any other scanner. + security-events: write + # Private-repo requirement for upload-sarif (workflow-run lookup). + actions: read