Practice — add SAST, SCA and image signing to a pipeline
This exercise turns Module 2 into a working, hardened pipeline. Starting from a basic build-and-deploy workflow, you will add the four controls the module introduced: least-privilege configuration, a SAST gate, an SCA gate, and container image signing with verification. The aim is a pipeline that refuses to ship code with serious source flaws, refuses to ship vulnerable dependencies, and refuses to run any image it cannot cryptographically trust.
Analogy🏏Cricket
🍳 Think of it like cooking: a professional kitchen does not pass a health inspection with one precaution — it layers them, checking the raw ingredients, the cook's own hygiene, and a seal on the finished dish. This exercise builds that layered kitchen for a pipeline: starting from a basic build-and-deploy workflow, you add least-privilege configuration, a SAST gate, an SCA gate, and container image signing with verification. The goal is a pipeline that refuses to ship code with serious source flaws, refuses to ship vulnerable dependencies, and refuses to run any image it cannot cryptographically trust. This reveals a secure pipeline as several independent checks, not one hopeful taste-test.
🏏 Showing the Cricket analogy — a Cricket version isn’t available for this concept yet.
The starting point is a simple workflow that checks out code, builds a container image, pushes it to a registry, and deploys it — with a broad default token and no security gates. Treat this as the insecure baseline. Your task is to layer controls onto it in order, verifying after each step that a deliberately introduced flaw is actually blocked, so you prove each gate works rather than merely assuming it does.
Analogy🏏Cricket
💼 Think of it like business: no auditor trusts a new control because the memo says it works — they plant a test transaction and confirm the control actually catches it before signing off. Your starting point is a simple workflow that checks out code, builds an image, pushes it, and deploys, all with a broad default token and no security gates: treat it as the insecure baseline. Layer controls onto it in order, and after each one deliberately introduce the flaw it should stop, verifying the gate really blocks it rather than assuming so. Proving each control on a planted flaw is what turns a checklist into evidence. This reveals verified enforcement, not mere configuration, as the standard a real audit demands.
🏏 Showing the Cricket analogy — a Cricket version isn’t available for this concept yet.
Step 1 — Lock down permissions and pin actions. Set the workflow's default token to read-only and grant each job only the specific permissions it requires. Replace any third-party actions referenced by tag with their full commit SHA. Confirm the change by checking that the build can no longer perform writes it does not explicitly request, closing the over-privileged default before you add anything that handles untrusted input.
Analogy🏏Cricket
💰 Think of it like finance: the first thing an incoming treasurer does is revoke the blanket signing authority everyone inherited and re-issue each role only the specific approval limit its job needs — and lock down which named counterparties can be paid at all. Step 1 does the same to the pipeline: set the workflow's default token to read-only, grant each job only the permissions it genuinely requires, and replace any third-party action referenced by a mutable tag with its full commit SHA so a hijacked tag cannot pay itself in. Confirm the build can no longer perform writes it never requested. This reveals closing the over-privileged default as the groundwork before any untrusted input is handled.
🏏 Showing the Cricket analogy — a Cricket version isn’t available for this concept yet.
yaml
# Step 1 — least privilege + pinned actionspermissions:contents:readjobs:build:permissions:contents:readpackages:write# only what the push step needssteps:-uses:actions/checkout@<full-sha>
Step 2 — Add the SAST and SCA gates. Insert a SAST scan of your source that fails the build on high-severity findings, then an SCA scan that generates an SBOM and blocks on known critical or high dependency vulnerabilities. Test both by introducing a flaw: add an obviously injectable code path and a deliberately outdated vulnerable library, and confirm each gate fails the build with a clear message before removing them.
Analogy🏏Cricket
⚽ Think of it like sports: before a match counts, officials check the equipment and referees trial a contentious call on video to be sure the system flags it — you never trust a rule until you have watched it catch a real offence. Step 2 adds two such officials: a SAST scan that fails the build on high-severity source findings, and an SCA scan that generates an SBOM and blocks on known critical or high dependency vulnerabilities. Prove both by planting offences — an obviously injectable code path and a deliberately outdated vulnerable library — and confirm each gate fails the build with a clear message before you remove them. This reveals a gate as trustworthy only once you have seen it whistle a genuine foul.
🏏 Showing the Cricket analogy — a Cricket version isn’t available for this concept yet.
bash
# Step 2 — SAST and SCA gates-name:SASTrun:semgrepci--configauto-name:SCArun:|syftdir:.-ocyclonedx-json>sbom.jsongrypesbom:sbom.json--fail-onhigh
Step 3 — Sign the image and enforce verification. After the image builds and passes the scans, sign its immutable digest with Cosign as the final trusted build step. Then configure deployment to verify that signature — ideally through an admission controller — so any image lacking a valid signature from your pipeline is rejected. Prove it by attempting to deploy an unsigned image and confirming the cluster refuses to schedule it.
Analogy🏏Cricket
💪 Think of it like fitness: a competition weightlifter's bar is certified and stamped before the lift, and the judges refuse to count any attempt on a bar that lacks the official mark — no stamp, no record. Step 3 gives your pipeline that stamp: after the image builds and passes the scans, sign its immutable digest with Cosign as the final trusted step, then configure deployment to verify that signature, ideally through an admission controller, so any image without a valid signature from your pipeline is rejected. Prove it by attempting to deploy an unsigned image and confirming the cluster refuses to schedule it. This reveals enforced verification as the judge that makes the signature actually mean something.
🏏 Showing the Cricket analogy — a Cricket version isn’t available for this concept yet.
bash
# Step 3 — sign on build, verify before deploy-name:Signimagerun:cosignsignregistry.example.com/app@${DIGEST}# Deploy side (admission policy) verifies:# cosign verify --certificate-identity-regexp '.../org/repo/.*' \# --certificate-oidc-issuer <ci-oidc-issuer> registry.example.com/app@${DIGEST}
Step 4 — Validate the whole chain and review. Run the finished pipeline end to end with clean code and confirm it succeeds, then reintroduce each planted flaw one at a time and confirm the matching gate blocks it: source flaw stops at SAST, vulnerable library stops at SCA, unsigned image stops at admission. Finally, note where a policy-as-code gate from Lesson 10 could enforce these steps' presence, so no future pipeline can quietly omit them.
Analogy🏏Cricket
♟️ Think of it like chess: before you trust an opening you do not just play it once and hope — you test it against every reply in turn, confirming each threatened line is answered, because one unguarded response loses the game. Step 4 validates the whole chain the same way: run the finished pipeline end to end with clean code and confirm it succeeds, then reintroduce each planted flaw one at a time and confirm the matching gate blocks it — source flaw stops at SAST, vulnerable library stops at SCA, unsigned image stops at admission. Finally note where a policy-as-code gate could enforce these steps' very presence. This reveals a defence as proven only when every attacking line has been met and answered.
🏏 Showing the Cricket analogy — a Cricket version isn’t available for this concept yet.
Start from an insecure baseline and layer controls, testing each with a planted flaw.
Step 1: default token to read-only, scope job permissions, pin actions to SHAs.
Step 2: SAST gate on source flaws, SCA gate on vulnerable dependencies.
Step 3: sign the image digest with Cosign and enforce verification at deploy.
Step 4: validate the full chain, then consider a policy-as-code gate to mandate these steps.