Skip to content

docs: add Secure Software Development Lifecycle policy (#3033) #33282

docs: add Secure Software Development Lifecycle policy (#3033)

docs: add Secure Software Development Lifecycle policy (#3033) #33282

Workflow file for this run

name: "CodeQL"
on:
push:
branches: ["main"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["main"]
types: [opened, labeled, unlabeled, synchronize]
# Code scanning and required checks must run on merge-queue commits too.
merge_group:
types: [checks_requested]
schedule:
# runs on 19:17 every Tuesday
- cron: "27 19 * * 2"
env:
# Use pipe fallback so transient proxy.golang.org 5xx errors during
# `go mod download` fall back to direct module fetches.
GOPROXY: "https://proxy.golang.org|direct"
# Least-privilege default; every job below declares its own narrower
# job-level permissions that override this baseline.
permissions:
contents: read
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
timeout-minutes: 360
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ["go", "javascript-typescript"]
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: block
allowed-endpoints: >
github.com:443
api.github.com:443
release-assets.githubusercontent.com:443
golang.org:443
google.golang.org:443
proxy.golang.org:443
storage.googleapis.com:443
modernc.org:443
sum.golang.org:443
storage.googleapis.com:443
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
category: "/language:${{matrix.language}}"
# The merge queue's ephemeral gh-readonly-queue/... ref is deleted as
# soon as the queue cycles, which routinely races the upload for this
# multi-minute analysis and fails it with "ref not found" if the
# action is left to auto-detect ref/sha from GITHUB_REF/GITHUB_SHA
# (the doomed ephemeral ref). Skipping the upload entirely (an
# earlier attempt at this fix) isn't right either: the native
# "CodeQL" required status check is only created when a SARIF
# upload actually happens, so skipping it left that required check
# permanently missing for merge_group commits -- the merge queue
# then waits out its full timeout for a check that will never
# report, exactly like the PR Semver Labels gap. Instead, point the
# upload at the queue's real target branch ref (which persists)
# while still analyzing the actual queued merge commit.
ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_ref || '' }}
sha: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || '' }}
# Unlike Dependabot/dependency-review (version-based), govulncheck does
# call-graph reachability analysis: it only flags CVEs in code this binary
# can actually reach, so findings here are high-confidence. Lives alongside
# lint-golangci below rather than its own file, matching that job's
# precedent for SARIF-producing Go tools that aren't CodeQL itself.
govulncheck:
name: govulncheck
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: block
allowed-endpoints: >
github.com:443
api.github.com:443
release-assets.githubusercontent.com:443
golang.org:443
go.googlesource.com:443
google.golang.org:443
proxy.golang.org:443
storage.googleapis.com:443
modernc.org:443
sum.golang.org:443
vuln.go.dev:443
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
- name: Run govulncheck
uses: golang/govulncheck-action@032d45514ae346b1db93c04b0c90b841c370344f # v1.1.0
env:
# Atmos ships with CGO disabled (see Makefile/test.yml) — without this,
# the runner's default CGO_ENABLED=1 makes govulncheck try to compile
# the cgo-only half of github.com/bearsh/hid (pulled in transitively via
# saml2aws's U2F support), which needs libudev-dev and fails to load.
CGO_ENABLED: "0"
# govulncheck's whole-program (`./...`) SSA call-graph analysis over
# this repo's large, dependency-heavy module (cloud SDKs, client-go,
# Terraform/OpenTofu, ...) peaks high enough in memory to get
# OOM-killed by the standard 16GB runner (observed as an unexplained
# SIGTERM/exit 143 partway through, with no findings ever produced).
# A soft memory ceiling forces the Go runtime to GC earlier/more
# often instead of letting the heap grow unchecked, trading some run
# time for staying under the runner's memory limit. GOFLAGS=-p=1
# (serializing package builds) was tried first and rejected: it only
# marginally reduced peak memory (govulncheck's SSA construction
# parallelizes via goroutines internally, which -p doesn't govern)
# while nearly tripling run time.
GOMEMLIMIT: "12GiB"
with:
go-version-file: go.mod
output-format: sarif
output-file: govulncheck.sarif
- name: Upload SARIF file
if: always()
uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
sarif_file: govulncheck.sarif
category: govulncheck
# See the ref/sha override comment on the analyze job's upload step
# above: without it, merge_group uploads race the ephemeral
# gh-readonly-queue/... ref being deleted and fail with "ref not found".
ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_ref || '' }}
sha: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || '' }}
semgrep:
name: Semgrep scan
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: block
allowed-endpoints: >
github.com:443
api.github.com:443
registry-1.docker.io:443
auth.docker.io:443
production.cloudfront.docker.com:443
semgrep.dev:443
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
# Run via `docker run` rather than a job-level `container:` so Harden
# Runner (which needs to instrument the runner VM directly) still works —
# job-level containers run every step inside the container instead.
- name: Run Semgrep
run: |
docker run --rm \
-v "${{ github.workspace }}:/src" \
-w /src \
semgrep/semgrep@sha256:65dcd4408adda7c183a6b4550cb1e9b19f7f627a6fbb7e0559bd466bedc44d7b \
semgrep scan --config=p/ci --sarif --output=semgrep.sarif --metrics=off
- name: Upload SARIF to code-scanning
if: always()
uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
sarif_file: semgrep.sarif
category: semgrep
# See the ref/sha override comment on the analyze job's upload step
# above: without it, merge_group uploads race the ephemeral
# gh-readonly-queue/... ref being deleted and fail with "ref not found".
ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_ref || '' }}
sha: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || '' }}
lint-golangci:
name: "Lint (golangci)"
runs-on:
- runs-on=${{github.run_id}}
- runner=terraform
- tag=atmos
- extras=s3-cache
- private=false
# golangci-lint-action supports merge_group with fetch-depth: 0 below,
# using the merge group's base revision to limit findings to new issues.
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
permissions:
# Required: allow read access to the content for analysis.
contents: read
# allow read access to pull request. Use with `only-new-issues` option.
pull-requests: read
# allow write access to checks to allow the action to annotate code in the PR.
checks: write
# allow write access to security events to allow the action to upload SARIF files.
security-events: write
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false
fetch-depth: 0
# golangci-lint-action@v4.0.0+ requires explicit Go setup
# Without this step, the action may fail intermittently with
# "could not load export data" errors due to cache corruption
- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0
with:
go-version-file: go.mod
cache: true
# Pre-populate module cache to prevent goanalysis_metalinter failures.
# Without this, the linter may fail with "could not load export data" errors.
# See: https://github.com/golangci/golangci-lint/issues/5437
- name: Download modules
run: go mod download
# Install the golangci-lint v2 CLI tool (not the linters themselves).
# This tool is needed to run `golangci-lint custom` which builds a custom binary
# that includes both standard linters AND our custom module plugins.
- name: Install golangci-lint v2 for custom build
run: |
# Pin to commit that fixes "-c advice.detachedHead=false" bug (PR #6206)
# https://github.com/golangci/golangci-lint/pull/6206
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@101ccaca0df22b2e36dd917ed5d0be423baa6298
# Build a custom golangci-lint binary with our lintroller plugin compiled in.
- name: Build custom golangci-lint with lintroller plugin
run: |
set -e
# Ensure lintroller module is ready
cd tools/lintroller && go mod tidy && cd ../..
# Build custom golangci-lint with plugins (staleness-guarded; see magefiles/)
go tool mage lint:customGCL
# Verify the custom binary was created
test -x ./custom-gcl || { echo "Error: custom-gcl not found or not executable"; exit 1; }
# Replace system binary with our custom one
mv ~/go/bin/golangci-lint ~/go/bin/golangci-lint.system
cp ./custom-gcl ~/go/bin/golangci-lint
chmod +x ~/go/bin/golangci-lint
golangci-lint --version
# Run the golangci-lint action using our custom binary with plugins.
#
# Key settings:
# - install-mode: none - Tells the action to use the binary already in PATH
# (our custom-gcl that we just installed)
# - only-new-issues: true - Only report issues in changed code
# - SARIF output - For GitHub Advanced Security / CodeQL integration
# - issues-exit-code: 0 - Always exit 0 so we upload SARIF to CodeQL
#
# The action provides:
# - Inline PR annotations for linting issues
# - Caching for faster runs
# - GitHub UI integration
#
# Our custom binary includes lintroller, so violations of:
# - os.MkdirTemp in tests (should use t.TempDir)
# - os.Setenv in tests (should use t.Setenv)
# - t.Setenv in defer blocks (should use os.Setenv)
# will appear in the SARIF output and GitHub Security tab.
- name: Run golangci-lint with lintroller plugin
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
version: 101ccaca0df22b2e36dd917ed5d0be423baa6298
install-mode: none
# The action's config verification downloads a remote JSON schema and
# can fail transiently. The subsequent lint command still parses and
# validates the configuration, so skip the duplicate network preflight.
verify: false
only-new-issues: true
args: >
--output.sarif.path=golangci-lint.sarif
--issues-exit-code=0
- name: Upload filtered SARIF results
# merge_group's ephemeral gh-readonly-queue/... ref is deleted as soon
# as the queue cycles, which routinely races this upload and fails it
# with "ref not found" if left to auto-detect ref/sha from
# GITHUB_REF/GITHUB_SHA (the doomed ephemeral ref). Point it at the
# queue's real target branch ref (which persists) while still
# uploading results for the actual queued merge commit, same as the
# CodeQL analyze step above -- skipping the upload outright isn't an
# option here either: it would leave the native "CodeQL" code-scanning
# required check without this SARIF's contribution.
if: always()
uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
with:
sarif_file: golangci-lint.sarif
ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_ref || '' }}
sha: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || '' }}
pr-semver-labels:
name: PR Semver Labels
# Required checks must also report on the merge queue's synthetic commit
# (github.event_name == 'merge_group'), otherwise the merge queue waits
# out its full check-response timeout for a check that will never exist,
# then dequeues the PR -- even when every other check already passed.
# The label check itself only makes sense against a real pull_request
# event (github.event.pull_request doesn't exist for merge_group), and
# by the time a PR reaches the queue its label was already verified on
# pull_request, so merge_group just needs this job to report a pass.
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: block
allowed-endpoints: >
api.github.com:443
github.com:443
# Checkout is required for local composite actions
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
if: github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]'
with:
persist-credentials: false
# Remove Dependabot's auto-added semver labels
# Dependabot PRs should only have the 'no-release' label since
# dependency updates don't determine Atmos releases.
- name: Clean up Dependabot labels
if: github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]'
uses: ./.github/actions/remove-dependabot-semver-labels
# Check for required semver labels
# Every PR must have exactly one: major, minor, patch, or no-release
# pull_request only: merge_group has no github.event.pull_request for
# this action to check labels against.
- uses: mheap/github-action-required-labels@23e10fde7e062233401931a0eece796cd9bf3177 # v5.6.0
if: github.event_name == 'pull_request'
with:
mode: exactly
count: 1
labels: |
major
minor
patch
no-release
add_comment: true
message: |
> [!WARNING]
> This PR is blocked from merging because a required semver label is missing.
> ```
> {{ provided }}
> ```
> You'll need to add one before this PR can be merged.