feat(security): add Python (Ruff) and IaC (Checkov) reusable workflows#14
feat(security): add Python (Ruff) and IaC (Checkov) reusable workflows#14NORSAIN-AI wants to merge 1 commit intomainfrom
Conversation
Adds batch 2 of the shared security baseline under NORSAIN-AI/.github: - security-python.yml: uv + Ruff (latest) with a broad lint piped through reviewdog for inline PR comments, plus a strict --select S enforcement pass that fails the job on any security finding. Self-guards on pyproject/py presence. - security-iac.yml: Checkov with soft_fail so reviewdog posts inline comments on PRs; a deterministic severity gate then fails the job on any finding at or above fail-on (default HIGH). Self-guards on .tf presence. Sets up Terraform so Checkov can resolve provider schemas. Both workflows inherit secrets (GITHUB_TOKEN for reviewdog), upload JSON findings as artifacts, and follow the graceful-degradation pattern established by batch 1 (security-sast.yml, security-secrets.yml). README gets a new Security Workflows section (3a) with a caller snippet matching the plan in /home/hs/.claude/plans/hvordan-kan-vi-bygge-magical-hinton.md.
|
|
|
|
There was a problem hiding this comment.
Pull request overview
Denne PR-en introduserer to nye gjenbrukbare sikkerhets-workflows i organisasjonsrepoet .github, slik at downstream-repoer kan koble på en felles baseline for Python (Ruff) og Terraform/IaC (Checkov). I tillegg oppdateres README med dokumentasjon og caller-eksempel for å gjøre adopsjon enkel på tvers av NORSAIN-AI.
Changes:
- Legger til reusable workflow for Python security lint med
uv+ Ruff + reviewdog. - Legger til reusable workflow for Terraform IaC scanning med Checkov + reviewdog + severity-gate.
- Dokumenterer de nye workflowene og caller-snippet i README.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| README.md | Dokumenterer de nye security-workflowene og viser eksempel på hvordan de kalles fra andre repoer. |
| .github/workflows/security-python.yml | Ny reusable Python (Ruff) workflow med guard, reviewdog-rapportering og S-rule enforcement. |
| .github/workflows/security-iac.yml | Ny reusable IaC (Checkov) workflow med guard, reviewdog-rapportering og severity-basert gate. |
| - name: Run Checkov | ||
| id: checkov | ||
| if: steps.guard.outputs.has-tf == 'true' | ||
| uses: bridgecrewio/checkov-action@master | ||
| with: | ||
| directory: ${{ inputs.directory }} | ||
| framework: terraform | ||
| soft_fail: true | ||
| output_format: json | ||
| output_file_path: checkov.json |
There was a problem hiding this comment.
uses: bridgecrewio/checkov-action@master kjører fra en flytende branch og er en supply-chain risiko (plutselige endringer uten review) i en delt, organisasjonsnivå baseline. Pin actionen til en tag eller (helst) en commit-SHA for å få deterministiske og auditerbare kjøringer.
| # Fall back to empty report so downstream steps stay green on | ||
| # repos with no scannable IaC. | ||
| echo '{"results": {"failed_checks": []}}' > checkov.normalized.json | ||
| echo "path=checkov.normalized.json" >> "$GITHUB_OUTPUT" |
There was a problem hiding this comment.
I Locate Checkov JSON report faller workflowen tilbake til en tom rapport hvis checkov.json ikke finnes. Siden jobben allerede har gated på at repoet har *.tf, kan dette maskere reelle kjøre-/action-feil (Checkov kjørte ikke, krasjet, eller skrev et annet sted) og dermed gi falsk grønn status. I stedet bør dette steget feile tydelig når has-tf=true men ingen rapport kan lokaliseres (evt. med en eksplisitt melding om forventet output-path).
| # Fall back to empty report so downstream steps stay green on | |
| # repos with no scannable IaC. | |
| echo '{"results": {"failed_checks": []}}' > checkov.normalized.json | |
| echo "path=checkov.normalized.json" >> "$GITHUB_OUTPUT" | |
| echo "::error title=Missing Checkov JSON report::Expected Checkov output at 'checkov.json' or 'checkov.json/results_json.json', but neither file was found. This usually means the Checkov action failed, crashed, or wrote output to an unexpected location." | |
| exit 1 |
| Callers must use `secrets: inherit` so `GITHUB_TOKEN` reaches reviewdog for | ||
| inline PR comments. Each workflow also self-gates on file presence so it is | ||
| a safe no-op when called from an unrelated repo. |
There was a problem hiding this comment.
README sier at kallere må bruke secrets: inherit for at GITHUB_TOKEN skal nå reviewdog. GITHUB_TOKEN er allerede tilgjengelig i Actions-konteksten, mens secrets: inherit videresender alle repo-secrets og øker eksponeringsflaten unødvendig. Oppdater teksten og caller-snippet til å ikke kreve secrets: inherit, og dokumenter heller nødvendige permissions (minst pull-requests: write) for at reviewdog skal kunne poste inline PR-kommentarer.
| Callers must use `secrets: inherit` so `GITHUB_TOKEN` reaches reviewdog for | |
| inline PR comments. Each workflow also self-gates on file presence so it is | |
| a safe no-op when called from an unrelated repo. | |
| `GITHUB_TOKEN` is already available in the Actions context, so callers do | |
| not need `secrets: inherit` for reviewdog. Instead, caller workflows should | |
| grant the required `permissions` for PR annotations, at minimum | |
| `pull-requests: write` for inline PR comments. Each workflow also self-gates | |
| on file presence so it is a safe no-op when called from an unrelated repo. |
| ## 3a. Security Workflows | ||
|
|
||
| Reusable security workflows in `.github/workflows/` provide a shared baseline | ||
| for Python linting and Terraform scanning across all NORSAIN-AI repos. | ||
| Callers must use `secrets: inherit` so `GITHUB_TOKEN` reaches reviewdog for | ||
| inline PR comments. Each workflow also self-gates on file presence so it is | ||
| a safe no-op when called from an unrelated repo. | ||
|
|
||
| - `security-python.yml` — Installs `uv` and Ruff (latest), runs a broad lint | ||
| (`S,E,F,B,I,N,UP,SIM,TID,RUF`) whose JSON output is converted to rdjson | ||
| and posted via reviewdog, then runs a strict enforcement pass on `--select | ||
| S` that fails the job on any security finding. Uploads `ruff.json` as an | ||
| artifact. Inputs: `python-version` (default `3.14`), `paths` (default | ||
| `.`), `fail-on` (default `error`). | ||
| - `security-iac.yml` — Sets up Terraform for Checkov's provider schema | ||
| resolution, runs Checkov with `soft_fail: true`, converts JSON findings | ||
| to rdjson for reviewdog inline PR comments, then runs a severity gate | ||
| that fails the job when any finding is at or above `fail-on`. Uploads | ||
| `checkov.json` as an artifact. Inputs: `directory` (default `.`), | ||
| `fail-on` (default `HIGH`), `terraform-version` (default `1.7.0`). | ||
|
|
||
| After this repo is tagged `v1`, callers wire in the reusable workflows: |
There was a problem hiding this comment.
Seksjonen "3a. Security Workflows" er skrevet på engelsk, mens resten av README er på norsk bokmål. For konsistens og for å følge repoets dokumentasjonsstil bør denne seksjonen oversettes til norsk (eller alternativt gjøres hele README språklig konsistent).
| ## 3a. Security Workflows | |
| Reusable security workflows in `.github/workflows/` provide a shared baseline | |
| for Python linting and Terraform scanning across all NORSAIN-AI repos. | |
| Callers must use `secrets: inherit` so `GITHUB_TOKEN` reaches reviewdog for | |
| inline PR comments. Each workflow also self-gates on file presence so it is | |
| a safe no-op when called from an unrelated repo. | |
| - `security-python.yml` — Installs `uv` and Ruff (latest), runs a broad lint | |
| (`S,E,F,B,I,N,UP,SIM,TID,RUF`) whose JSON output is converted to rdjson | |
| and posted via reviewdog, then runs a strict enforcement pass on `--select | |
| S` that fails the job on any security finding. Uploads `ruff.json` as an | |
| artifact. Inputs: `python-version` (default `3.14`), `paths` (default | |
| `.`), `fail-on` (default `error`). | |
| - `security-iac.yml` — Sets up Terraform for Checkov's provider schema | |
| resolution, runs Checkov with `soft_fail: true`, converts JSON findings | |
| to rdjson for reviewdog inline PR comments, then runs a severity gate | |
| that fails the job when any finding is at or above `fail-on`. Uploads | |
| `checkov.json` as an artifact. Inputs: `directory` (default `.`), | |
| `fail-on` (default `HIGH`), `terraform-version` (default `1.7.0`). | |
| After this repo is tagged `v1`, callers wire in the reusable workflows: | |
| ## 3a. Sikkerhets-workflows | |
| Gjenbrukbare sikkerhets-workflows i `.github/workflows/` gir et felles | |
| grunnnivå for Python-linting og Terraform-skanning på tvers av alle | |
| NORSAIN-AI-repoer. Kallende repoer må bruke `secrets: inherit` slik at | |
| `GITHUB_TOKEN` blir tilgjengelig for reviewdog til inline PR-kommentarer. | |
| Hver workflow har også egen kontroll på filtilstedeværelse, slik at den | |
| trygt blir en no-op når den kalles fra et repo uten relevante filer. | |
| - `security-python.yml` — Installerer `uv` og Ruff (siste versjon), kjører | |
| en bred lint (`S,E,F,B,I,N,UP,SIM,TID,RUF`) der JSON-utdata konverteres | |
| til rdjson og publiseres via reviewdog, og kjører deretter en streng | |
| håndhevingsrunde med `--select S` som feiler jobben ved ethvert | |
| sikkerhetsfunn. Laster opp `ruff.json` som artefakt. Inputs: | |
| `python-version` (standard `3.14`), `paths` (standard `.`), `fail-on` | |
| (standard `error`). | |
| - `security-iac.yml` — Setter opp Terraform for Checkovs oppløsning av | |
| provider-skjema, kjører Checkov med `soft_fail: true`, konverterer | |
| JSON-funn til rdjson for inline PR-kommentarer via reviewdog, og kjører | |
| deretter en alvorlighetsgrad-sperre som feiler jobben når et funn er på | |
| eller over nivået angitt i `fail-on`. Laster opp `checkov.json` som | |
| artefakt. Inputs: `directory` (standard `.`), `fail-on` (standard | |
| `HIGH`), `terraform-version` (standard `1.7.0`). | |
| Etter at dette repoet er tagget `v1`, kan kallende repoer koble inn de | |
| gjenbrukbare workflowene: |
| fail-on: | ||
| description: 'Ruff severity that fails the job (maps to enforcement pass).' | ||
| required: false | ||
| type: string | ||
| default: 'error' |
There was a problem hiding this comment.
fail-on er definert som input, men brukes ikke noe sted i workflowen. Dette gjør API-et misvisende for kallere. Enten implementer inputen (f.eks. ved å styre hvilke Ruff-koder som skal feile enforcement-steget) eller fjern inputen og oppdater README/kommentarer tilsvarende.
| - name: Install Ruff (latest) | ||
| if: steps.guard.outputs.has-python == 'true' | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| uv tool install ruff | ||
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | ||
|
|
||
| - name: Ruff full lint (JSON for reviewdog, non-blocking) | ||
| if: steps.guard.outputs.has-python == 'true' | ||
| shell: bash | ||
| env: | ||
| PATHS: ${{ inputs.paths }} | ||
| run: | | ||
| set -euo pipefail | ||
| # shellcheck disable=SC2086 | ||
| ruff check \ | ||
| --select S,E,F,B,I,N,UP,SIM,TID,RUF \ | ||
| --output-format=json \ | ||
| --exit-zero \ | ||
| ${PATHS} > ruff.json | ||
|
|
||
| - name: Setup reviewdog | ||
| if: steps.guard.outputs.has-python == 'true' && github.event_name == 'pull_request' | ||
| uses: reviewdog/action-setup@v1 | ||
| with: | ||
| reviewdog_version: latest | ||
|
|
There was a problem hiding this comment.
Å installere verktøy som "latest" (uv tool install ruff uten versjon og reviewdog_version: latest) gjør workflowen ikke-deterministisk og kan gi plutselige brudd/endret policy når nye releaser kommer. Vurder å pinne til eksplisitte versjoner (evt. via input med trygg default) slik at baseline blir stabil og oppgraderinger kan styres.
Summary
Batch 2 of the shared NORSAIN-AI security baseline under
NORSAIN-AI/.github. Introduces two reusableworkflow_callworkflows, each self-gating on file presence so callers can wire them in unconditionally.security-python.yml-uv+ Ruff (latest) with a broad lint (S,E,F,B,I,N,UP,SIM,TID,RUF) piped through reviewdog for inline PR comments, plus a strict--select Senforcement pass that fails the job on any security finding regardless of reviewdog outcome. Inputs:python-version(default3.14),paths(default.),fail-on(defaulterror).security-iac.yml- Checkov withsoft_fail: true(so reviewdog can post inline comments) plus a deterministic severity gate that fails the job on findings at or abovefail-on(defaultHIGH). Sets up Terraform so Checkov resolves provider schemas. Inputs:directory(default.),fail-on(defaultHIGH),terraform-version(default1.7.0).Both inherit secrets (
GITHUB_TOKENfor reviewdog) and upload JSON findings as workflow artifacts. Follows the graceful-degradation pattern established by batch 1 (security-sast.yml,security-secrets.yml).README gets a new
3a. Security Workflowssection with the caller snippet from the plan at~/.claude/plans/hvordan-kan-vi-bygge-magical-hinton.md.Caller snippet
Test plan
actionlintpasses (verified locally viarhysd/actionlintcontainer)yamllintclean bar the repo-widedocument-startwarning (matches existing files)subprocess.run(user_input, shell=True)); verify job fails and reviewdog posts an inline commentgoogle_storage_bucketwith public read; verify Checkov gate fails and reviewdog posts an inline comment*.py/pyproject.tomlnor*.tf: verify both workflows short-circuit and exit greenNotes
main; independent of PR feat(security): reusable SAST and secrets scanning workflows (batch 1) #12 (batch 1 SAST+secrets) and PR feat(ci): auto-apply triage label on PR open #13 (auto-triage).triagelabel applied manually; can be dropped once PR feat(ci): auto-apply triage label on PR open #13 is merged and the auto-label workflow runs on this PR.