How to explain to students
Open with the cost-of-a-bug curve: the earlier you catch a security issue, the cheaper it is to fix. Caught at code-write time (IDE warning) → 5 minutes. Caught in CI → 30 minutes. Caught in production → days of incident response, customer notifications, possibly regulatory fines. "Shift-left" means moving security checks earlier in the pipeline, where they're cheap.
DevSecOps is not "now we have a security team". It's "every engineer owns security as part of their workflow." The DevOps engineer's job: make the right thing easy — pre-commit hooks that catch secrets, CI jobs that scan images, dashboards that show drift.
🎯 Practice Questions
Show Answer
Two one-afternoon implementations:
1. Add pre-commit hooks with
gitleaks + trufflehog to block AWS keys / API tokens before they hit the repo. Free, ~10 lines of .pre-commit-config.yaml.2. Add a Trivy step in CI that scans every Docker image build and fails the pipeline on HIGH/CRITICAL CVEs. Free, ~5 lines of GitHub Actions YAML.
Both prevent classes of incidents, both work the day they're added, both cost zero.
How to explain to students
Three rules, in order of impact: (1) Never commit a secret to git, (2) never bake a secret into a Docker image, (3) always rotate. .env files on a developer laptop are fine; in production, secrets live in AWS Secrets Manager or SSM Parameter Store, fetched at startup.
The first line of defense is a pre-commit hook that scans staged files. gitleaks and trufflehog both detect AWS keys, GitHub tokens, Stripe keys, etc. — install once, blocks 99% of accidental commits.
🎯 Practice Questions
AWS_SECRET_ACCESS_KEY=abc123, force-pushes 30 seconds later. List the right order of operations.Show Answer
2. Audit usage. CloudTrail → look for any API call from unfamiliar IPs in the last hour. Limit blast radius if any.
3. Then clean git history.
git filter-repo or BFG to scrub the secret from all branches and force-push.4. Add a pre-commit hook so it can't happen again.
5. Postmortem. Fix the process — why was the secret in code at all?
Force-pushing without rotating first is the #1 mistake. The window between commit and force-push is enough.
How to explain to students
Every Docker image is a stack of "someone else's code" — your base image (Ubuntu, Alpine), your language runtime (Node, Python), your dependencies (npm, pip). Each layer ages and accumulates known vulnerabilities (CVEs). Trivy compares the installed packages against a CVE database and reports what's affected.
Run Trivy twice: once locally before push, once in CI. CI is the gate — fail the build on HIGH/CRITICAL findings. The local run is for fast feedback while iterating.
🎯 Practice Questions
Show Answer
1. Are the vulnerabilities reachable in your code path? Many CVEs in transitive deps are in code you never call. Use
--reachability-style tools (Snyk, Semgrep Pro) to triage.2. Is there a workaround? Pin to an older patched version, swap the dep, sandbox the affected component.
If genuinely unfixable + reachable, the right call is risk-acceptance with documentation, not silent ignore.
The flag:
--ignore-unfixed tells Trivy "skip CVEs with no available fix." Useful for keeping CI green while you wait for upstream patches — but document which ones you're skipping (use .trivyignore with comments and expiry dates).
trivy fs in addition to trivy image? What does each find that the other misses?trivy fs --severity CRITICAL . before every commit. Sketch the pre-commit config.How to explain to students
SAST (Static Application Security Testing) = analyse source code without running it. Finds patterns: SQL string-concatenation, unescaped user input, weak crypto. Tools: Semgrep, CodeQL, language-specific linters (eslint-plugin-security for JS, bandit for Python).
DAST (Dynamic Application Security Testing) = attack the running app like a black-box pentester would. Sends malformed inputs, tries SQL injection, XSS, auth bypass. Tools: OWASP ZAP, Burp. Slower than SAST but finds real-world issues SAST misses (e.g. config-level holes).
🎯 Practice Questions
Show Answer
(b) DAST — actively probes the running app's
/login endpoint with malformed inputs.(c) DAST — header presence is a runtime/config concern; SAST can't see what your reverse proxy or app emits in actual responses.
(d) SAST — looks at the import + call site of
crypto.MD5 in source.Rule of thumb: code-level pattern → SAST. Behaviour-level / response-header / config issue → DAST.
How to explain to students
The OWASP Top 10 is a regularly-updated list of the most prevalent web vulnerability classes. You don't have to be a pen-tester — but you must recognise these by name and know which DevOps controls mitigate each. A01 Broken Access Control is #1 for a reason: nearly every breach involves auth gone wrong.
🎯 Practice Questions
Show Answer
1. Dependabot (or Renovate) — opens PRs the moment a dependency has a new version with a security fix. Free on GitHub.
2. Trivy / Snyk in CI — fails the build if HIGH/CRITICAL CVEs land in
package.json, requirements.txt, or the Docker image.3. SBOM generation + storage — keep a record of every dependency shipped to prod (
trivy --format cyclonedx). When a new CVE drops in 6 months, you can answer "are we affected?" in 30 seconds.Bonus: npm audit / pip-audit as a pre-merge gate. Free, fast, catches the common case.
How to explain to students
Modern apps depend on hundreds of packages, written by people you've never met, hosted on infra you don't control. Supply-chain attacks compromise that chain — a malicious commit in a package that 10,000 apps install. The classic examples: event-stream (npm, 2018), SolarWinds (2020), log4shell (2021). Defending against this is a DevOps responsibility.
Three control layers: (1) Pin versions in lock files — never use floating ranges in CI. (2) Auto-update via Dependabot / Renovate — security patches arrive within hours. (3) Verify integrity — signed commits (Sigstore), signed Docker images (Cosign), SBOM diffs in PR review.
🎯 Practice Questions
npm ci in CI safer than npm install for supply-chain protection?Show Answer
Keyless Cosign (Sigstore): at sign time, the CI runner asks the OIDC provider (e.g. GitHub Actions) for a short-lived token proving "this build ran in repo X on branch main." Sigstore mints a signing certificate bound to that identity, valid for ~10 minutes.
Result: there is no key to steal. To forge a signature, an attacker would need to compromise GitHub Actions and your repo — both of which have separate audit trails. This is the modern recommended practice.
How to explain to students
AI is excellent at brainstorming attack scenarios ("here's my architecture, what could go wrong?"), reviewing IAM policies for over-permission, and explaining CVEs in plain language. It is not a substitute for a real security review on safety-critical changes — but it's a great force multiplier on day-to-day work.
Trap: AI sometimes invents CVE numbers or claims a non-existent attack works. Verify against MITRE / NVD before quoting a CVE in a postmortem. And never paste real production secrets, customer data, or unfixed-vuln details to a public AI tool.
🎯 Practice Questions
Show Answer
1. Defence in depth — "requires admin" assumes nobody else gets admin. In a real breach, an attacker chains multiple bugs: the SSRF gives them an internal cred → which gives them admin → which lets them exploit "the bug that requires admin." Each layer matters.
2. AI is not your authoritative source. AI summarises training data; it doesn't have visibility into your specific deployment, threat model, or compliance constraints. Use the AI take as a hypothesis, not a verdict.
Always cross-check against the actual CVE description (CVSS score, attack vector, complexity) and your team's threat model.
How to explain to students
By this point, students have a Node app + Dockerfile + GitHub Actions pipeline (from earlier modules). Now they bolt on the full DevSecOps layer: pre-commit secret scan, npm audit gate, Trivy scan, Semgrep SAST, OWASP ZAP DAST, Cosign signing, and Secrets Manager for production credentials. The artefact: a hardened pipeline where pushing a known vulnerability is impossible.
Sample quiz questions (interactive)
Fill-in-the-command
myapp:1.0 and exit non-zero on HIGH or CRITICAL.myapp/prod/db from AWS Secrets Manager via the CLI.Assignment
📋 Assignment Requirements
- Pick one of your earlier projects (Docker, CI/CD, AWS — any with code and a Dockerfile)
- Add a
.pre-commit-config.yamlrunning gitleaks; commit and verify it blocks a fake AWS key - Add a security workflow (
.github/workflows/security.yml) with at minimum: gitleaks, npm audit (or pip-audit), Semgrep SAST, Trivy image scan - Each step must fail the build on findings of HIGH or above — no warnings-only passes
- Move at least one production secret to AWS Secrets Manager (or SSM Parameter Store), document the rotation policy in the README
- Add Dependabot config for the relevant ecosystem(s)
- Run the workflow against a known-bad commit (paste a fake AWS key, install
lodash@4.17.10) and screenshot the failed runs - Map the controls you added to OWASP categories — write a
SECURITY.mdtable with at least 5 entries - Bonus: Add Cosign keyless signing to the image build job
- Bonus: Add an OWASP ZAP baseline scan against a staging deploy
- Bonus: Generate + commit an SBOM via Trivy or Syft, document how to query it for a future CVE