DevSecOps: security in the pipeline
Shift security left: scan code, dependencies, secrets, images and infrastructure in every pipeline, protect the software supply chain, and keep pipeline credentials short-lived.
- Explain “shift left” and where each kind of security scan fits in a pipeline
- Protect the software supply chain with SBOMs, signing and pinned dependencies
- Keep secrets out of code and give pipelines short-lived, least-privilege credentials
Byte Bakery’s security review used to happen once a year, by a team who saw the code for the first time the week before launch. Now the code changes 30 times a day. DevSecOps builds security into the pipeline itself - shifting left, so problems are found minutes after they’re written, by the person who wrote them, when they’re cheapest to fix.
| Check | Finds | When |
|---|---|---|
| Secret scanning | keys and passwords committed to code | pre-commit hook and every push |
| SAST (static analysis) | risky code: SQL injection, unsafe deserialization | every pull request |
| SCA (dependency scanning) | libraries with known vulnerabilities (CVEs) | every build, and daily - new CVEs appear in old code |
| Image scanning | vulnerable OS packages in container images | after the image build |
| IaC scanning | public buckets, open security groups in Terraform | every pull request |
| DAST (dynamic testing) | vulnerabilities in the running app | against staging |
Protecting the supply chain
- Pin dependencies and actions to exact versions or commit SHAs, and review dependency updates (tools like Dependabot and Renovate open them as pull requests).
- Generate an SBOM (software bill of materials, in SPDX or CycloneDX format) for each artifact, so when the next Log4Shell lands you can answer “are we affected?” in minutes.
- Sign artifacts (for example with Sigstore’s cosign) and verify signatures before deploying; frameworks like SLSA describe levels of build integrity and provenance.
- Give pipelines short-lived credentials: instead of storing a cloud access key as a CI secret, let the CI job exchange an OIDC token for temporary credentials limited to exactly what it needs.
- Enforce rules automatically with policy as code (Open Policy Agent, Kyverno): “no images from unknown registries”, “no containers running as root”.
1import re
2
3AWS_KEY = re.compile(r"AKIA[0-9A-Z]{16}")
4lines = [
5 'DRONE_API = "https://drones.bytebakery.example"',
6 'aws_key = "AKIAIOSFODNN7EXAMPLE"',
7]
8for number, line in enumerate(lines, start=1):
9 for match in AWS_KEY.finditer(line):
10 print(f"line {number}: AWS access key {match.group()[:4]}****{match.group()[-4:]}")line 2: AWS access key AKIA****MPLE
Try it
Which check catches it?
Sort each problem into the check most likely to catch it first.
“A Stripe API key pasted into settings.py”
“An SQL query built by concatenating user input”
“A logging library version with a remote-code-execution CVE”
“An outdated OpenSSL package in the container’s base image”
“A Terraform S3 bucket with public read access”
Key takeaways
Shift left: secret scanning, SAST, SCA, image and IaC scanning run in every pipeline.
Your pipeline and dependencies are part of the attack surface - pin, review, sign and keep an SBOM.
Use short-lived, least-privilege credentials (OIDC) instead of long-lived keys in CI.
A leaked secret must be revoked and rotated, not just deleted.
Lesson quiz
7 questions · pass with 5 correct · up to 50 XP
Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.
Practice: automate DevOps chores in Python
Write the small Python tools DevOps teams really build - pipeline runners, plan checkers, metric calculators, scanners - and run them against sample inputs. They run locally in your browser; no servers or cloud accounts needed.
Build a secret scanner
The input is files: a header line == path, then that file’s lines. Scan every line (numbered from 1 within its file) with these rules, in this order:
aws-access-key:AKIA[0-9A-Z]{16}github-token:ghp_[A-Za-z0-9]{36}private-key:-----BEGIN [A-Z ]*PRIVATE KEY-----password-assignment:(password|passwd|secret)\s*[:=]\s*["'][^"']{4,}["'], ignoring case
Skip lines containing # nosecret. Print each finding as config/settings.py:3 aws-access-key AKIA****MPLE - the match masked as its first 4 characters, **** and its last 4. Finish with 3 findings in 2 files: block the merge or no secrets found.
- Byte Bakery repo
- Clean
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Gate the build on vulnerabilities
The first input line is today’s date (YYYY-MM-DD). Then come findings, package version severity cve fixed_in (fixed_in may be none), then ---, then risk-accepted exceptions: cve expires_on reason....
Decide each finding:
criticalorhighwith a fix available:BLOCK- unless an exception for that CVE hasn’t expired yet (its date is today or later), which makes itACCEPTED until 2026-12-01; an expired exception is noted asBLOCK (exception expired 2026-09-01).criticalorhighwith no fix:WARN (no fix yet - mitigate).mediumorlow:INFO.
Print findings by severity (critical, high, medium, low), then package, as BLOCK doughlib 2.1.0 CVE-2026-1001 (critical, fixed in 2.1.4), putting any parenthesized note after the decision word and the details last, like WARN (no fix yet - mitigate) yeastyhttp 0.9.2 CVE-2026-3003 (high). Finish with pipeline: failed (2 blocking) or pipeline: passed.
- Nightly scan
- Clean enough
Python runs in a sandboxed browser worker with a 60 second time limit. Its runtime loads from the Pyodide CDN; your code stays in this browser.
Questions about this lesson
Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.
Loading posts…