June 9, 2026
Security Spec-Driven Development (SSDD)- The next evolution of AppSec in the AI era
When I wrote about Spec-Driven Development in November 2025, I left one critical question unanswered: where does security fit?
Jagan Raj Raviraja
8 min read
The answer is the same place the spec lives: at the beginning, before the AI writes a single line of code. AI agents generate exactly what they are asked to build. If security requirements do not exist in the specification, they will not exist in the software. That is the core problem SSDD solves
The problem with traditional AppSec
For years, security teams have followed the same familiar pipeline:
This model worked when humans translated requirements into code slowly enough for reviews to keep pace. AI changes that completely. A developer can generate thousands of lines in minutes. Features that once took days can be built before a security review is even scheduled.
The faster software is generated, the earlier security decisions must be made. The traditional code-review-centric model cannot keep up.
AI is good at building features, not understanding risk
Consider a simple prompt: "Build a file upload service." Most AI coding assistants will generate a working implementation. Users can upload files. Files are stored. The feature works.
But without explicit security requirements, the generated code may have none of these:
This is not an AI problem. It is a specification problem. The requirement did not exist, so neither did the control.
Introducing [SSDD] โ Security Spec-Driven Development
SSDD extends SDD by embedding security requirements directly into specifications before code generation begins. Security is no longer a validation step. It becomes a design input.
Now the AI has context beyond functionality. It understands the constraints that matter. Security is not an afterthought. It is part of the intended output.
To make this concrete, here is the same file upload feature generated twice: once without a security spec, and once with one:
Without security spec:
// No security requirements in the spec: AI generates a working but vulnerable handler
app.post('/upload', async (req, res) => {
const file = req.files.upload;
const uploadPath = `./uploads/${file.name}`;
await file.mv(uploadPath);
res.json({ status: 'uploaded', path: uploadPath });
});
// No auth check. No file type check. No size limit.
// Path traversal possible via file.name. No malware scan.// No security requirements in the spec: AI generates a working but vulnerable handler
app.post('/upload', async (req, res) => {
const file = req.files.upload;
const uploadPath = `./uploads/${file.name}`;
await file.mv(uploadPath);
res.json({ status: 'uploaded', path: uploadPath });
});
// No auth check. No file type check. No size limit.
// Path traversal possible via file.name. No malware scan.With security spec:
// Security requirements in the spec: AI generates a constrained, secure handler
app.post('/upload', authenticate, async (req, res) => {
const file = req.files.upload;
// Control: file type allowlist from spec
const allowed = ['application/pdf', 'image/png', 'image/jpeg'];
if (!allowed.includes(file.mimetype))
return res.status(400).json({ error: 'File type not permitted' });
// Control: max size 10 MB from spec
if (file.size > 10_485_760)
return res.status(400).json({ error: 'File exceeds size limit' });
// Control: sanitise filename to prevent path traversal
const safeName = path.basename(file.name).replace(/[^a-zA-Z0-9._-]/g, '_');
const uploadPath = path.join(`./uploads/${req.user.tenantId}`, safeName);
// Control: malware scan before persisting
const scanResult = await malwareScanner.scan(file.data);
if (!scanResult.clean)
return res.status(400).json({ error: 'File failed security scan' });
await file.mv(uploadPath);
res.json({ status: 'uploaded' });
});
// Same feature. Radically different output.
// Every control above maps directly to a line in the SSDD spec.// Security requirements in the spec: AI generates a constrained, secure handler
app.post('/upload', authenticate, async (req, res) => {
const file = req.files.upload;
// Control: file type allowlist from spec
const allowed = ['application/pdf', 'image/png', 'image/jpeg'];
if (!allowed.includes(file.mimetype))
return res.status(400).json({ error: 'File type not permitted' });
// Control: max size 10 MB from spec
if (file.size > 10_485_760)
return res.status(400).json({ error: 'File exceeds size limit' });
// Control: sanitise filename to prevent path traversal
const safeName = path.basename(file.name).replace(/[^a-zA-Z0-9._-]/g, '_');
const uploadPath = path.join(`./uploads/${req.user.tenantId}`, safeName);
// Control: malware scan before persisting
const scanResult = await malwareScanner.scan(file.data);
if (!scanResult.clean)
return res.status(400).json({ error: 'File failed security scan' });
await file.mv(uploadPath);
res.json({ status: 'uploaded' });
});
// Same feature. Radically different output.
// Every control above maps directly to a line in the SSDD spec.The second handler is not the result of a smarter AI. It is the result of a more complete specification. Every security control traces back to a requirement that was written before generation began.
The Security Spec-Driven Development Lifecycle (SSDDLC)
The lifecycle looks similar to SDD but adds security as a first-class concern at every stage, from requirements through to audit.
Business and security requirements flow in as two separate tracks and merge into a single unified spec before any code is generated. When validation fails, the fix goes to whoever owns the gap: a missing control loops back to the spec, a gap in the threat model loops back to the security engineer.
SSDD in practice
The lifecycle describes the process. But what does SSDD actually look like when a team runs it day to day? Below is the sequence of interactions between the three key actors: the security engineer, the developer working with an AI agent, and the CI/CD pipeline that enforces the spec automatically.
The feedback loops in the sequence below are where SSDD pays off most. In a traditional pipeline, a failed security review means going back to a developer who has already moved on to the next feature. The context is lost. The fix is rushed. In SSDD, the loop is tight and automatic: the CI/CD pipeline catches the gap immediately after generation and routes it to the right person. A missing implementation goes back to the developer. An incomplete threat model goes back to the security engineer. No ambiguity about who owns what.
Three actors, one source of truth. The security engineer shapes the spec upfront. The developer and AI agent build against it. The CI/CD pipeline enforces it automatically, looping back to the right level when something is missing.
Notice that the unified spec is the only shared artifact. There is no handoff document, no security sign-off email, no review ticket that sits in a queue. The spec is the contract. The pipeline is the enforcer. Security becomes a property of the build process, not a gate at the end of it.
This also changes how security debt accumulates. In traditional teams, security debt builds silently in code that was never reviewed. In SSDD, debt is surfaced at generation time, before it ever reaches production. The cost of fixing a missing control drops from a post-release patch to a spec update and a regeneration run.
Threat models as machine-readable artifacts
This is the most forward-looking implication of SSDD, and the one most worth unpacking.
Today, threat models live in documents, spreadsheets, and slide decks. They are useful for humans. They are invisible to machines. In an SSDD world, threat models need to become structured inputs that AI agents, CI/CD pipelines, and development tools can consume directly.
This is not a new idea from scratch. The Open Threat Modeling Format (OTM), an open standard developed by IriusRisk, already provides a tool-agnostic, JSON and YAML based structure for representing threat models in a machine-readable way. It defines components, data flows, trust boundaries, threats, and mitigations in a format that can be version-controlled, parsed by tools, and consumed by automated pipelines. SSDD builds directly on this foundation.
Here is what a simplified OTM-style threat model looks like for our file upload service:
{
"otmVersion": "0.2.0",
"project": {
"name": "File Upload Service",
"id": "file-upload-service"
},
"trustZones": [
{ "id": "authenticated", "name": "Authenticated Users Only", "risk": { "trustRating": 50 } }
],
"components": [
{
"id": "upload-handler",
"name": "Upload Handler",
"type": "service",
"trustZone": "authenticated"
}
],
"threats": [
{
"id": "threat-01",
"name": "Malicious file upload",
"categories": ["tampering"],
"component": "upload-handler",
"mitigations": ["mit-01", "mit-02"]
},
{
"id": "threat-02",
"name": "Path traversal via filename",
"categories": ["elevation-of-privilege"],
"component": "upload-handler",
"mitigations": ["mit-03"]
},
{
"id": "threat-03",
"name": "Oversized file denial of service",
"categories": ["denial-of-service"],
"component": "upload-handler",
"mitigations": ["mit-04"]
}
],
"mitigations": [
{ "id": "mit-01", "name": "File type allowlist: pdf, png, jpg", "riskReduction": 80 },
{ "id": "mit-02", "name": "Malware scan before persisting", "riskReduction": 90 },
{ "id": "mit-03", "name": "Sanitise filename, strip path separators", "riskReduction": 95 },
{ "id": "mit-04", "name": "Enforce 10 MB max upload size", "riskReduction": 85 }
]
}{
"otmVersion": "0.2.0",
"project": {
"name": "File Upload Service",
"id": "file-upload-service"
},
"trustZones": [
{ "id": "authenticated", "name": "Authenticated Users Only", "risk": { "trustRating": 50 } }
],
"components": [
{
"id": "upload-handler",
"name": "Upload Handler",
"type": "service",
"trustZone": "authenticated"
}
],
"threats": [
{
"id": "threat-01",
"name": "Malicious file upload",
"categories": ["tampering"],
"component": "upload-handler",
"mitigations": ["mit-01", "mit-02"]
},
{
"id": "threat-02",
"name": "Path traversal via filename",
"categories": ["elevation-of-privilege"],
"component": "upload-handler",
"mitigations": ["mit-03"]
},
{
"id": "threat-03",
"name": "Oversized file denial of service",
"categories": ["denial-of-service"],
"component": "upload-handler",
"mitigations": ["mit-04"]
}
],
"mitigations": [
{ "id": "mit-01", "name": "File type allowlist: pdf, png, jpg", "riskReduction": 80 },
{ "id": "mit-02", "name": "Malware scan before persisting", "riskReduction": 90 },
{ "id": "mit-03", "name": "Sanitise filename, strip path separators", "riskReduction": 95 },
{ "id": "mit-04", "name": "Enforce 10 MB max upload size", "riskReduction": 85 }
]
}This OTM file is consumed by the AI agent as context before it generates code. Each threat maps to one or more mitigations, and each mitigation becomes a concrete control the agent must implement. The categories (tampering, denial-of-service, elevation-of-privilege) map directly to STRIDE, the industry standard threat classification model, giving the AI a shared vocabulary for understanding the nature of each risk.
After generation, the CI/CD pipeline reads the same OTM file and validates that every mitigation has a corresponding implementation. If mit-03 (filename sanitization) is absent from the code, the build fails with a precise message pointing back to the threat it was supposed to address. No ambiguity. No manual triage.
Because OTM is version-controlled alongside the code, the threat model evolves with the product. When a new abuse case is discovered in production, a new threat entry is added, mitigations are defined, and the next generation run must account for them. Threat modeling stops being a one-time workshop and becomes a living, continuously enforced artifact.
How SSDD changes the role of security engineers
Security engineers spend less time reviewing implementation and more time authoring the security layer of specifications. Their focus shifts toward writing security requirements, defining trust boundaries, building abuse case libraries, and designing policy guardrails that constrain AI-generated code.
They become reviewers of intent, not reviewers of code. That is a profound shift in how application security operates, and in where security expertise creates the most value.
Why this matters now
The industry talks constantly about AI increasing developer productivity. That is true. But productivity without security creates risk at scale. The faster software can be generated, the more important it becomes to get security right before generation begins.
Organizations adopting AI-assisted development without evolving their security practices risk generating vulnerabilities as fast as they generate features. SSDD offers a different path: scale development while maintaining security intent.
In an AI-native world, intent becomes one of the most important assets an engineering team owns. SSDD is the framework for making that intent explicit and secure.
Where to start
SSDD does not require a platform overhaul or a new security tool. It starts with a habit: writing security requirements before you write a prompt to your AI agent.
A practical first step is to pick your single highest-risk feature (the one where a breach would hurt most) and write an SSDD spec for it. Define the trust boundary: who is allowed to use this feature and under what conditions? List three to five abuse cases: what would an attacker try? Map the controls: what does the code need to enforce for each one? Then give that spec to your AI agent alongside the functional requirements and observe what changes in the output.
From there, the patterns become reusable. Abuse cases you write for one feature apply to others. Security controls become a library. Threat model entries start to look the same across similar features and can be templated. What starts as a manual discipline gradually becomes an engineering standard embedded in your development workflow.
The tooling to enforce this at CI/CD level is still maturing, but the spec itself costs nothing except the time to write it. That is where most teams should start: not with a new platform, but with a new question asked at the beginning of every feature: what could go wrong, and how does the spec prevent it?
Looking ahead
Spec-Driven Development changed the question from "how do we write code?" to "how do we define software?"
Security Spec-Driven Development pushes that idea one step further. It asks: "how do we define secure software?"
As AI agents become more capable, the future of application security may not be centered around reviewing code at all. It may be centered around reviewing specifications. The organizations that understand this shift early will not simply build software faster. They will build secure software by design.
Key takeaways
- AI agents generate exactly what is specified, not what is assumed. Security requirements must be explicit.
- Traditional code-review AppSec cannot keep pace with AI-generated software at scale.
- SSDD embeds security requirements, threat assumptions, and abuse cases directly into the specification.
- Threat models are evolving from human-readable documents into machine-readable, executable artifacts.
- Security engineers are shifting from reviewers of implementation to authors of intent: shaping specs, not scanning code.
About the Author
I'm Jagan Raj, a tech enthusiast and cybersecurity specialist with hands-on experience securing applications, AI/LLM systems, and cloud-based products. I'm passionate about turning complex security challenges into clear, practical solutions and helping teams build software that's both scalable and secure.