# DevSecOps fundamentals

> Explain shift-left and shared ownership, map security controls onto every SDLC phase, and set triage rules a team will actually follow.

DevSecOps Engineer roadmap · Foundations · 15 min read · https://levelcode.org/roadmaps/devsecops/devsecops-fundamentals

DevOps made shipping fast: small changes, automated pipelines, many deploys a day. Security that still works like a quarterly sign-off from a separate team can't keep up, so it either gets bypassed or becomes the bottleneck. DevSecOps is the fix: build security checks into the same automated flow that builds and ships the code, and make them everyone's job. In this lesson you'll learn the core principles, map concrete controls onto each phase of the software lifecycle, and set the rules for when a finding blocks a merge and who owns fixing it.

## From gatekeeper to guardrails

In the traditional model, developers build a feature for weeks, then hand it to a security team for a penetration test just before release. Three things go wrong:

- **Late feedback.** A design flaw found a week before launch is expensive to fix: the code is written, other code depends on it, and the deadline is fixed. Teams accept the risk because the alternative is missing the date.
- **Lost context.** The developer who wrote the vulnerable code moved on weeks ago. Fixing it means relearning their own work.
- **A bottleneck.** One security engineer typically supports dozens or hundreds of developers. Manual review of every release doesn't scale.

DevSecOps replaces the single gate with **guardrails**: many small, automated checks that run where the work happens, give feedback in minutes, and stop only the problems that clearly matter. A secret scanner in a pre-commit hook tells you about a leaked key before it leaves your laptop. A dependency scanner on a pull request tells you a library has a known critical vulnerability while you still remember why you added it.

**Shift left** is the slogan for this: move security activities earlier (to the left on a timeline running from plan to production). It does not mean "only test early". Some problems only show up in a running system, so you also **shift right**: monitor production, collect security logs, and respond to incidents. A mature program does both.

> [!NOTE]
> DevSecOps is not a tool you install. Tools are the easy part. The hard part is agreeing who fixes what, how fast, and when a pipeline is allowed to say no.

## Principles that guide every decision

A handful of principles come up in every later lesson. Learn them by name; you'll use them to argue for or against designs.

| Principle | Meaning | Example |
|---|---|---|
| **Least privilege** | Every person, service and token gets only the access it needs, for only as long as it needs it. | A CI job that runs tests gets read-only repository access, not admin. |
| **Defense in depth** | Layer independent controls so one failure isn't a breach. | Parameterised queries *and* a database user that can't drop tables. |
| **Secure by default** | The easy path is the safe path. | A service template that ships with TLS, security headers and non-root containers already on. |
| **Fail closed** | When a check can't decide, deny. | If the authorization service times out, reject the request rather than allow it. |
| **Minimise attack surface** | Fewer exposed features, ports and dependencies means fewer things to attack. | Remove the unused admin endpoint instead of protecting it. |
| **Assume breach** | Design as if an attacker is already inside one component. | Short-lived credentials and audit logs, so a stolen token is limited and traceable. |

Behind these sits the **CIA triad**, the three properties security protects: **confidentiality** (only authorised parties read data), **integrity** (data and code aren't changed without authorisation) and **availability** (the system keeps working). Every vulnerability you meet threatens at least one of them, which is a useful way to describe impact.

## The secure SDLC, phase by phase

The software development lifecycle (SDLC) has the same phases whether you run Scrum, Kanban or something else. A secure SDLC attaches specific controls to each one. This table is the map for the rest of this roadmap.

| Phase | Security activity | What it catches | Typical tooling |
|---|---|---|---|
| Plan / design | Threat modeling, security requirements | Design flaws: missing authorization, trust in client input | Whiteboard, data-flow diagrams, STRIDE |
| Code | Secure coding standards, IDE linting, pre-commit hooks | Injection, hard-coded secrets, unsafe APIs | Linters, gitleaks |
| Commit / review | Code review with a security checklist, branch protection | Logic bugs, risky changes to auth or CI config | Pull requests, CODEOWNERS |
| Build | SAST, dependency scanning (SCA), SBOM generation | Known-vulnerable libraries, dangerous code patterns | Semgrep, CodeQL, Trivy, Dependabot |
| Test | DAST, security unit tests | Misconfigured headers, auth bypass in the running app | OWASP ZAP, integration tests |
| Release | Artifact signing, provenance | Tampered builds, unknown artifact origin | Sigstore cosign, SLSA provenance |
| Deploy | IaC scanning, policy as code, least-privilege deploy credentials | Public buckets, open security groups, over-privileged roles | Checkov, OPA, OIDC to the cloud |
| Operate | Logging, alerting, patching, incident response | Active attacks, newly disclosed vulnerabilities | SIEM, runtime detection, on-call runbooks |

The earliest phases catch the most expensive problems: no scanner finds "users can see each other's invoices" as reliably as ten minutes of threat modeling. And each control catches a different class of problem, so they complement each other.

## A worked example: one bug, many layers

Follow a single mistake through a pipeline with these controls. A developer adds an endpoint that exports a customer's orders as CSV, and makes three errors: the SQL query is built with string formatting, the endpoint doesn't check that the order belongs to the caller, and a test API key is pasted into a config file.

```text
Developer laptop
  pre-commit: gitleaks ............ BLOCKS commit: "generic-api-key in config/dev.py:12"
              -> key moved to .env (git-ignored), commit retried

Pull request
  CI job "sast" (Semgrep) ......... FAILS: "sqlalchemy raw SQL with string formatting"
              -> query rewritten with bound parameters, job passes
  CI job "sca" .................... passes (no vulnerable dependencies)
  Code review (CODEOWNERS for /api/) reviewer asks: "where is the ownership check?"
              -> WHERE order.customer_id = :current_user added, test written

Staging
  DAST baseline scan .............. WARN: missing Content-Security-Policy header
              -> ticket created, fixed next sprint (medium severity)

Production
  Access logs + alerting .......... would flag one user exporting thousands of orders
```

No single layer caught everything. The secret scanner can't spot a missing authorization check; the reviewer is unlikely to notice a high-entropy string in a 400-line diff; the header problem only exists in a running app. This is defense in depth applied to the delivery process itself.

Notice also what *didn't* happen: no meeting, no ticket to a security team, no week of waiting. Each fix happened within minutes of the mistake, by the person who made it.

## Shared ownership in practice

"Security is everyone's job" turns into "security is nobody's job" unless you make ownership explicit. Teams that do this well usually have:

- **A platform or security team that builds paved roads.** They maintain the CI templates, base images and libraries that make the secure path the default. Their customers are developers.
- **Development teams that own their findings.** A vulnerability in a service is that service team's bug, prioritised like any other bug, with a deadline set by severity.
- **Security champions.** One developer per team with extra training and a direct line to the security team. They triage scanner output, run threat-modeling sessions and spread knowledge. It's a role, not a job title.

Write the rules down. The most important rule is the **remediation SLA**: how long a finding of each severity may stay open. A common starting point:

| Severity | Example | Blocks merge? | Fix within |
|---|---|---|---|
| Critical | Exploitable remote code execution, leaked production credential | Yes | 24 hours (rotate secrets immediately) |
| High | SQL injection, authentication bypass | Yes | 7 days |
| Medium | Missing security header, verbose error messages | No | 30 days |
| Low | Informational findings, hardening suggestions | No | Next planned work, or accept |

> [!TIP]
> Blocking only critical and high findings on new code keeps pipelines trustworthy. If every low-severity warning fails the build, developers learn to click "re-run" or to disable the job, and you lose the high-severity signal along with the noise.

### Exceptions with an expiry date

Sometimes a finding is a false positive, or the risk is genuinely acceptable for now. Record that decision instead of silently ignoring the alert. Most scanners support an ignore file; keep the reason, an owner and an expiry date next to each entry, and review them:

```yaml
# security-exceptions.yaml (a team convention, reviewed monthly)
- id: CVE-2026-12345
  package: libxml-parse 2.4.1
  reason: Vulnerable function is only reachable via XML import, which is disabled in our build.
  owner: payments-team
  approved_by: security-champion@payments
  expires: 2026-12-31
```

When the date passes, the exception lapses and the finding blocks again. This turns "we'll deal with it later" into a decision someone made, with a deadline.

## Measuring whether it works

What you measure shapes behaviour. Counting raw findings rewards running fewer scanners. Better metrics describe speed and coverage:

- **Mean time to remediate (MTTR)** per severity. Is it within your SLA?
- **Coverage.** What share of repositories run secret scanning, SAST and dependency scanning on every pull request?
- **Age of the open backlog.** Old high-severity findings mean the process is broken somewhere.
- **Escaped defects.** Vulnerabilities found in production (by pentests, bug bounty or incidents) that an earlier control should have caught. Each one is a prompt to add or tune a control.

## Common pitfalls

- **Tool sprawl.** Five overlapping scanners produce duplicate, conflicting findings. Start with one tool per category and tune it.
- **Blocking on noise.** A scanner turned on at full strictness across a legacy codebase produces thousands of findings on day one. Block on *new* findings in changed code first, and burn down the existing backlog separately.
- **Security as a checkbox.** A green pipeline means "no known patterns were found", not "secure". Threat modeling and review still matter.
- **Forgetting the pipeline itself.** CI systems hold deploy credentials and can push to production. They are a high-value target and need the same least privilege and review as application code.

## Practice

Pick a repository you work on (or any open-source project you know well). Create a one-page security baseline for it:

1. For each SDLC phase in the table above, write down which control exists today, or "none".
2. Identify the two biggest gaps, and say which kind of problem each gap lets through.
3. Propose a remediation SLA table for the team and decide which severities block a merge.
4. Write one exception entry for a finding you'd accept, including an expiry date.

**Solution**

A worked baseline for a typical small web API (Python, deployed to a cloud provider from CI):

| Phase | Control today |
|---|---|
| Plan / design | None; features go straight to tickets |
| Code | Linter (style only) |
| Commit / review | Pull requests required, one approval, no CODEOWNERS |
| Build | Dependabot alerts enabled but not reviewed; no SAST |
| Test | Unit tests only |
| Release | None |
| Deploy | CI uses a long-lived cloud access key with admin rights |
| Operate | Application logs, no security alerting |

**Biggest gaps:**

1. *Deploy credentials.* An admin-level, long-lived key in CI means anyone who can change a workflow (or compromise a dependency that runs in CI) can take over the cloud account. Fix: OIDC federation with a role scoped to deployment only.
2. *No design review.* Authorization flaws (IDOR, missing role checks) are invisible to the current controls. Fix: a 30-minute threat model for any feature that touches auth, payments or personal data.

**SLA:** critical 24 h, high 7 days, medium 30 days, low best effort; critical and high block merges on new findings.

**Exception:**

```yaml
- id: python.flask.security.audit.debug-enabled.debug-enabled
  path: scripts/local_dev_server.py
  reason: Local-only helper, never deployed; excluded from the container image.
  owner: api-team
  expires: 2027-03-31
```

## Key takeaways

- DevSecOps builds automated, fast-feedback security checks into the delivery pipeline and makes developers owners of their findings.
- Shift left to catch problems cheaply, and shift right with monitoring and incident response for what only appears in production.
- Least privilege, defense in depth, secure defaults and fail-closed behaviour are the principles behind almost every control.
- Map controls to every SDLC phase; each catches a different class of problem, so they layer rather than replace each other.
- Block merges only on high-confidence, high-severity findings, set remediation SLAs, and record exceptions with an owner and an expiry date.
- Treat the CI/CD system itself as production infrastructure: it holds the keys.

## Quiz

1. What does "shift left" mean in DevSecOps?

   A. Move security activities earlier in the lifecycle so problems are found when they're cheapest to fix
   B. Hand all security work to the operations team
   C. Stop testing in production entirely
   D. Run security scans only on the main branch after merge

   **Answer: A.** Shift left means earlier feedback: threat modeling at design, scanning at commit and pull request. It complements, rather than replaces, production monitoring (shift right).

2. A team turns on a SAST scanner at maximum strictness and makes every finding fail the build. After a month, developers routinely disable the job. What went wrong?

   A. SAST tools should never run in CI
   B. Blocking on low-confidence and low-severity noise destroyed trust; only high-severity findings on new code should block
   C. The scanner should have run only once a quarter
   D. Developers should not be able to see scanner results

   **Answer: B.** When most failures are noise, people route around the check and lose the important signal too. Block on high-severity, high-confidence findings in changed code and burn down the rest separately.

3. Which control is most likely to catch a missing ownership check (users can read each other's invoices)?

   A. A pre-commit secret scanner
   B. Dependency scanning (SCA)
   C. Container image scanning
   D. Threat modeling and code review of the authorization logic

   **Answer: D.** Authorization logic flaws are business-logic bugs. Pattern-based scanners rarely understand who should own which record; humans reasoning about the design and the diff catch them.

4. Why should a risk-acceptance exception include an expiry date?

   A. Scanners reject ignore entries without dates
   B. Expired exceptions are automatically fixed by the tool
   C. So the decision is revisited and the finding blocks again if nobody renews it
   D. It makes the finding count as remediated in metrics

   **Answer: C.** Without an expiry, "accept for now" silently becomes "ignore forever". A date forces someone to re-decide with current information.
