Skip to content

chore(decdn_node): sync config-schema surface to decdn main @ 0b94efe… #106

chore(decdn_node): sync config-schema surface to decdn main @ 0b94efe…

chore(decdn_node): sync config-schema surface to decdn main @ 0b94efe… #106

Workflow file for this run

---
name: CI
on:
push:
branches: [main]
pull_request:
# Least privilege by default; jobs widen only what they need.
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# Third-party actions are pinned to a full commit SHA (a re-pointed tag can ship
# malicious code — cf. the March 2026 KICS action compromise). The trailing
# comment records the human-readable version; .github/dependabot.yml bumps them.
jobs:
# Detect whether ansible/ changed so the heavy Ansible jobs skip unrelated PRs.
changes:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
ansible: ${{ steps.filter.outputs.ansible }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: filter
with:
filters: |
ansible:
- 'ansible/**'
# Ansible style + best-practice + the production-profile SECURITY rules,
# plus a syntax-check of every playbook. Runs only when ansible/ changed.
ansible-lint:
needs: changes
if: needs.changes.outputs.ansible == 'true'
runs-on: ubuntu-latest
defaults:
run:
working-directory: ansible
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
- name: Install Ansible tooling
run: python -m pip install --upgrade ansible ansible-lint yamllint
- name: Install Galaxy collections
run: make deps # must precede lint/syntax-check
- name: Lint (yamllint + ansible-lint)
run: make lint
- name: Syntax-check playbook
# Dummy inventory: no real hosts.yml is committed upstream (only hosts.yml.example),
# and --syntax-check only parses, it never connects.
run: ansible-playbook playbooks/site.yml --syntax-check -i localhost,
# Build the public `decdn.node` collection and run galaxy-importer's checks —
# the same validation Galaxy runs on upload (metadata, license, README, embedded
# ansible-lint). This is a readiness GATE only: it never publishes and needs no
# token. Runs only when ansible/ changed.
galaxy-build:
needs: changes
if: needs.changes.outputs.ansible == 'true'
runs-on: ubuntu-latest
defaults:
run:
working-directory: ansible
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
- name: Install build + import tooling
run: python -m pip install --upgrade ansible-core ansible-lint galaxy-importer
- name: Vendor collection dependencies
# So galaxy-importer's embedded ansible-lint can resolve the roles' FQCNs
# (devsec.hardening, ansible.posix). Installed under ansible/collections.
run: make deps
env:
ANSIBLE_COLLECTIONS_PATH: collections
- name: Build + validate the decdn.node collection
run: make galaxy-check
env:
ANSIBLE_COLLECTIONS_PATH: collections
- name: Upload collection artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: decdn-node-collection
path: ansible/build/decdn-node-*.tar.gz
if-no-files-found: ignore
# Dedicated IaC security scan of the Ansible tree, driven straight from the
# digest-pinned KICS *engine* image by `make security` — the exact command
# developers run locally, so CI and local results cannot drift. KICS severities
# are CRITICAL/HIGH/MEDIUM/LOW/INFO; the engine's own `--fail-on high` exit
# code is the gate.
#
# SUPPLY-CHAIN NOTE: we deliberately do NOT use Checkmarx/kics-github-action.
# Its git tags were hijacked in the March 2026 TeamPCP attack (CISA KEV), and
# beyond that history its entrypoint `apk add`s nodejs/npm at *run* time inside
# its digest-pinned base image and then executes the result — an unpinned fetch
# that defeats the pinning it advertises. That fetch also breaks the action
# outright today: Chainguard's current nodejs wants a newer glibc than the
# pinned base ships, so `node dist/index.js` dies and the step exits non-zero
# no matter what the scan found. Driving the engine image ourselves drops the
# Node layer entirely and leaves one pinned artifact — `KICS_IMAGE` in the
# Makefile, pinned by Docker Hub digest (a different artifact from the
# hijacked action). See CONTRIBUTING.md.
kics:
needs: changes
if: needs.changes.outputs.ansible == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: KICS Ansible security scan (fail on HIGH)
run: make security
# Replaces the action's `enable_jobs_summary`.
- name: Summarise KICS findings
if: always()
run: |
results=kics-results/results.json
{
echo "### KICS IaC scan"
echo
if [ -f "$results" ]; then
jq -r '.severity_counters
| "| CRITICAL | HIGH | MEDIUM | LOW | INFO |",
"|---|---|---|---|---|",
"| \(.CRITICAL) | \(.HIGH) | \(.MEDIUM) | \(.LOW) | \(.INFO) |"' "$results"
echo
jq -r 'if (.total_counter // 0) == 0 then "No findings."
else (.queries[] | .query_name as $q | .severity as $s
| .files[] | "- **\($s)** \($q) — `\(.file_name):\(.line)`")
end' "$results"
else
echo "No results file — the scan did not complete."
fi
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload KICS results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: kics-results
path: kics-results/
if-no-files-found: ignore
# To surface KICS findings in the repo's Security tab, enable GitHub code
# scanning (free on public repos) and un-comment the block below (add
# `security-events: write` to this job's permissions):
# - name: Upload SARIF to code scanning
# if: always()
# uses: github/codeql-action/upload-sarif@d77b13a0df3134d64a457ea9003f600b09fa1c8a # v3.36.1
# with:
# sarif_file: kics-results/results.sarif
# Lint the workflow files themselves.
actionlint:
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: reviewdog/action-actionlint@d290e336d5a743810aef4404f757dc862276d2ae # v1.73.4
with:
reporter: github-check
fail_on_error: true