# Secure software development lifecycle

Translate policy into testable product requirements. Trace owner, acceptance criteria, release gates and incident feedback in one record.

Original AI-assisted SecNet educational content. Fictional local examples; not a standards provider publication, endorsement or compliance evidence. Page exercises do not create account progress.

Sources checked: 2026-10-21

## Trace the requirement

“The app must be secure” is not testable. “No role may read a foreign tenant’s record without explicit cross-tenant authority” is a testable invariant. The business owner owns scope, engineering implementation, QA negative cases and AppSec risk reasoning. Link a requirement ID to code, tests and release evidence; a checklist alone does not establish that trace.

## Gates and exceptions

Define release evidence before development begins. A deadline is not acceptance rationale for an open critical issue. Where risk acceptance is permitted, record authorized owner, expiry, compensating controls and review date; acceptance does not mean fixed. The model’s no-open-critical rule is an educational policy, not a universal organizational risk model.

## From incident to prevention

Security work does not end when an incident ticket closes. Link the root cause to a missing requirement, design boundary or retest gap. Add a regression test that catches recurrence and assign maintenance ownership. NIST SSDF 1.1 is final; 1.2 / SP800-218r1 is draft at this source check. Do not label a draft as an effective final requirement.

## Example requirement record

For HD-REQ-01, define the invariant “north/ada reads only tickets it owns within north.” The product owner defines exceptions, engineering implements the server predicate, QA tests owner/other-user/other-tenant/anonymous cases, and AppSec records assumptions and residual risk. The release owner checks evidence for the same revision. If administrators later gain cross-tenant support authority, do not silently bypass HD-REQ-01: introduce explicit authority, rationale, audit records and tests. This prevents a requirement change from disappearing as removal of a SQL filter. After an incident, review not only closure but also why the regression test missed the failure.

## Working sequence

1. Write product-specific invariants.
2. Assign owners and acceptance tests.
3. Define release evidence and exception authority.
4. Bind the release decision to the same revision.
5. Turn incidents into regression tests.

## 1. From policy to acceptance

A policy document without an owner and test evidence does not prove implementation. The model requires the complete traceable requirement.

This code is a local policy model; it does not replace target/device testing.

### Weak implementation
```js
s=>s.policyExists
```

### Corrected implementation
```js
s=>s.policyExists&&s.ownerAssigned&&s.acceptanceDefined&&s.testEvidence
```

### Synthetic input and actual model output
```json
{
  "before": [
    {
      "name": "policy-only",
      "input": {
        "policyExists": true,
        "ownerAssigned": false,
        "acceptanceDefined": false,
        "testEvidence": false
      },
      "expected": false,
      "observed": true,
      "matched": false
    },
    {
      "name": "traceable-requirement",
      "input": {
        "policyExists": true,
        "ownerAssigned": true,
        "acceptanceDefined": true,
        "testEvidence": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    }
  ],
  "after": [
    {
      "name": "policy-only",
      "input": {
        "policyExists": true,
        "ownerAssigned": false,
        "acceptanceDefined": false,
        "testEvidence": false
      },
      "expected": false,
      "observed": false,
      "matched": true
    },
    {
      "name": "traceable-requirement",
      "input": {
        "policyExists": true,
        "ownerAssigned": true,
        "acceptanceDefined": true,
        "testEvidence": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    }
  ]
}
```

## 2. Deadline pressure

A deadline does not remove a critical finding. Require tests and authorized approval together; neither a checkbox nor green tests suffice alone.

This code is a local policy model; it does not replace target/device testing.

### Weak implementation
```js
s=>s.deadlineToday
```

### Corrected implementation
```js
s=>s.criticalOpen===0&&s.requiredTests&&s.approverAuthorized
```

### Synthetic input and actual model output
```json
{
  "before": [
    {
      "name": "deadline-pressure",
      "input": {
        "deadlineToday": true,
        "criticalOpen": 1,
        "requiredTests": false,
        "approverAuthorized": true
      },
      "expected": false,
      "observed": true,
      "matched": false
    },
    {
      "name": "evidenced-release",
      "input": {
        "deadlineToday": true,
        "criticalOpen": 0,
        "requiredTests": true,
        "approverAuthorized": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    },
    {
      "name": "unauthorized-approval",
      "input": {
        "deadlineToday": true,
        "criticalOpen": 0,
        "requiredTests": true,
        "approverAuthorized": false
      },
      "expected": false,
      "observed": true,
      "matched": false
    }
  ],
  "after": [
    {
      "name": "deadline-pressure",
      "input": {
        "deadlineToday": true,
        "criticalOpen": 1,
        "requiredTests": false,
        "approverAuthorized": true
      },
      "expected": false,
      "observed": false,
      "matched": true
    },
    {
      "name": "evidenced-release",
      "input": {
        "deadlineToday": true,
        "criticalOpen": 0,
        "requiredTests": true,
        "approverAuthorized": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    },
    {
      "name": "unauthorized-approval",
      "input": {
        "deadlineToday": true,
        "criticalOpen": 0,
        "requiredTests": true,
        "approverAuthorized": false
      },
      "expected": false,
      "observed": false,
      "matched": true
    }
  ]
}
```

## 3. A recurring incident

Do not equate a closed ticket with prevention. Without root cause, regression and ownership, the same failure may recur.

This code is a local policy model; it does not replace target/device testing.

### Weak implementation
```js
s=>s.incidentClosed
```

### Corrected implementation
```js
s=>s.incidentClosed&&s.rootCauseReviewed&&s.regressionAdded&&s.ownerAssigned
```

### Synthetic input and actual model output
```json
{
  "before": [
    {
      "name": "ticket-only",
      "input": {
        "incidentClosed": true,
        "rootCauseReviewed": false,
        "regressionAdded": false,
        "ownerAssigned": false
      },
      "expected": false,
      "observed": true,
      "matched": false
    },
    {
      "name": "prevention-loop",
      "input": {
        "incidentClosed": true,
        "rootCauseReviewed": true,
        "regressionAdded": true,
        "ownerAssigned": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    }
  ],
  "after": [
    {
      "name": "ticket-only",
      "input": {
        "incidentClosed": true,
        "rootCauseReviewed": false,
        "regressionAdded": false,
        "ownerAssigned": false
      },
      "expected": false,
      "observed": false,
      "matched": true
    },
    {
      "name": "prevention-loop",
      "input": {
        "incidentClosed": true,
        "rootCauseReviewed": true,
        "regressionAdded": true,
        "ownerAssigned": true
      },
      "expected": true,
      "observed": true,
      "matched": true
    }
  ]
}
```

## Exit evidence

- Requirement-code-test-release traceability table
- Expiring risk acceptance and regression ownership

## References

- [NIST SSDF publications: 1.1 final; 1.2 draft](https://csrc.nist.gov/projects/ssdf/publications)
- [OWASP ASVS 5.0.0](https://asvs.owasp.org/)
