# Building an Application Security Program That Works

> Source: <https://collinsthoughts.substack.com/p/building-an-application-security>
> Published: 2026-09-15 01:36:35+00:00

If an Application Security program were a chair, its strength would depend on four main legs. Remove one and the whole thing wobbles. Remove two and it collapses.

Too many programs still run like it’s 2008: endlessly triaging scanner output, filing Jira tickets, and chasing down single vulnerabilities like they’re snowflakes. That’s not AppSec, that’s project management with extra steps. It burns out security teams, slows engineering, and doesn’t move the risk needle.

The real mission of AppSec isn’t to babysit scanners. It’s to:

- Turn security from a blocker into an accelerator
- Frame security as an enabler of speed and reliability, not just compliance
- Fix systemic flaws at scale, not just one-off bugs

Here’s how to build a program that stands on four solid legs and the AI tooling that makes it runnable by a small team.

## 🪑 Leg 1: Security by Default

The easiest way to scale is to make the secure path the default path. Guardrails, not gates.

**SAST guardrails, tiered.** Three rule tiers, not two:

Tier TP behavior Use when **Monitor** Logged in your findings backlog. No PR action. Rule still being tuned; FP rate unknown **Comment** AI adds a PR comment with the vulnerable code and remediation. Build passes. Devs should see it but it shouldn’t block **Block** PR comment + CI soft-block until remediated *or* a 50+ char justification comment is left. Rule has proven FP rate <5% over a quarter

The soft-block-with-justification keeps you out of speed-bump territory while preserving accountability. Every override is auditable. Every override feeds the loop.

**AI runs** **between** **rule evaluation and PR action — never in front of SAST.** Every finding, even on a Block rule, passes through an AI FP filter using accumulated pattern memory, repo-level context files (`CLAUDE.md`, READMEs), and the PR diff itself. Devs see only high-signal findings.

**Custom rules for your codebase.** Default rulesets are noise. Write rules for *your* anti-patterns,  webhook handlers must have signature verification, any controller without auth middleware, dangerous primitives that should route through a library, and *positive* requirements like “this file must import the auth-helper.”

**Dependency management.** Renovate or Dependabot with a tiered policy: auto-merge patches on trusted packages, weekly batched minors, scheduled majors, immediate PR + 7-day SLA on CVEs. Auto-merge on patches is the single biggest quality-of-life unlock for an engineering org.

**Default threat modeling.** Threat modeling used to live in Leg 2 as a reactionary activity: pull in security, sit through a kickoff, push through review. Most projects skipped it. Build an AI assistant with two persistent reference files (a STRIDE rubric, a standardized output template) and wire it to a Slack intake channel. Every new project gets a STRIDE-scored model at planning, automatically. The high-risk ones still get a human pass in Leg 2,  now from 100% coverage instead of whatever crossed your desk.

**Fill the gaps with paved roads.** If you see the same flaw class three times, don’t file three tickets, build the safe primitive. An `http_client` wrapper that rejects RFC 1918 ranges and enforces timeouts. An auth helper that handles tenant scoping. Then write the SAST rule that flags any use of the underlying unsafe primitive outside the wrapper. The vulnerability becomes structurally impossible to reintroduce.

*Mindset shift: stop fixing bugs. Start removing bug classes.*

## 🪑 Leg 2: Reactionary Security

Defaults will not catch everything. This leg is for finding what made it through — without becoming a speed bump on every project.

**Deep-dive threat modeling.** Reserve human threat-modeling for projects flagged High by the default modeler in Leg 1. Don’t redo the whole assessment; extend it. **Threat model a problem and build outwards.** Pick a real risk, model it, let the model define the work. A threat model that catalogues every conceivable abuse case is an artifact. A threat model that says “this is what gets us breached, here’s who owns each mitigation” is leverage.

**Penetration testing.** Annual external is table stakes, scoped to the highest-risk surface (auth, payments, regulated data  (PHI / PII / PCI). s

The bigger unlock is **AI-driven continuous validation** between annual pentests. The pattern that works:

1. **Run multiple disjoint AI scans** of the repo with different scoping. Coverage compounds; dedupe on Finding URL.
2. **First-pass validation against live** `main`**.** Open each finding at` file:line` , trace data flow, check the framework controls the analyst typically misses (ORM-level authorization, route auth middleware, row-level security, internal-only exposure layers). Assign one of six verdicts: TP / Likely-TP / Unclear / Likely-FP / Fixed / File-Gone.
3. **Routing reachability classification** on HIGH+CRITICAL: Public / Authenticated / Internal / Dead. A “Critical” sitting behind a network-ACL’d endpoint is not Critical.
4. **CVSS re-scoring** with C/I/A capped at the validated verbal severity.
5. **End-to-end chain re-trace.** Re-verify each exploit chain link against current`main` — route still mounted, sink reachable, preconditions achievable in prod. Mark each INTACT / PARTIAL / BROKEN.

On a recent run, this pipeline collapsed 19 Critical findings to 1. **AI scanners are pattern matchers; pattern presence ≠ reachability.** First-pass answers “is the code as described?” Chain re-trace answers “does this fire in prod today?” The second answer is where the false-Critical count falls off.

**Bug bounty.** Useful for surface coverage and creative attacks, but only if you can close the loop. Phase it: invite-only private (10–20 researchers) until you can hit sub-48h triage reliably, then go public. *Triage SLA matters more than bounty amount.* Researchers will return to a program that pays median bounties with 24h triage. They won’t return to one that pays double with 2-week triage.

**DAST.** The hardest reactionary tool to wire into CI. Two patterns work: ephemeral preview environments + scheduled scans, or production-safe authenticated DAST against staging on a nightly cron. Don’t try to make DAST blocking. Almost nobody gets it right.

*Rules of engagement: don’t be a speed bump. Prioritize exploitable, high-impact issues. Feed every finding back into systemic fixes.*

## 🪑 Leg 3: Security by Design

This leg is about embedding security into how teams build. Done right, it looks like good engineering.

**Pattern libraries.** Don’t just publish a doc. Ship reference implementations, starter templates, SDKs. Each pattern in your library answers four questions:

- *What it is* - the secure approach
- *Why* - the threat it mitigates and the failure mode it prevents
- *Don’t* - the unsafe equivalent, with a real example from your codebase
- *When to deviate* - and what to do instead

The *why* is the durable part. “Use this” gets forgotten. “Use this because otherwise the next person to add tenant scoping can bypass the check” sticks. Anchor patterns to your stack, not to OWASP generically. “How authorization actually works in our ORM” is what gets used.

**Security champions.** One engineer per product team, designated as the security point. They get a private Slack channel, monthly sync, first dibs on conference budget, early access to new tooling, and credit visible to their managers. The unspoken contract: they advocate for security in their team’s planning, you back them up with air-cover. Highest-leverage thing in the leg.

**Education that works.** Annual compliance training is mostly theater. Three things actually move the needle:

- **Onboarding** within 30 days, scoped to your stack — paved-road libraries, top 3 anti-patterns you’ve already seen, how to file a security question.
- **PR-comment teaching.** When AI or a human flags an unsafe pattern, link the pattern library entry. The teaching happens at the moment of the mistake, which is the only moment it lands.
- **Framework migration moments.** When teams move from Rails to a new framework or REST to GraphQL, write a one-page migration security guide. Engineers are most receptive to new mental models when they’re already rebuilding their existing ones.

**Actionable principles.** Saltzer & Schroeder’s 1975 design principles: least privilege, fail-safe defaults, complete mediation, separation of duties are timeless. Ross Anderson synthesized them into the modern field in *Security Engineering*. The difference is operationalization: least privilege in IAM configs, defense in depth with layered service auth, fail-safe defaults in feature flags.

*The goal isn’t to make every engineer a security engineer. It’s to compress the gap between what an engineer reaches for naturally and what the security team would have done.*

## 🪑 Leg 4: Continuous Feedback & Metrics

The final leg closes the loop. Without feedback, programs stagnate. With it, every triage outcome makes the system smarter on your codebase.

AI is the mechanism that makes this loop continuous at scale. It does the linear work: filtering findings, refreshing dashboards, assessing projects so humans can do the work that scales with judgment.

**Metrics that matter.** Drop vanity metrics like “vulnerabilities found.” Track:

- **Reduction in recurring flaw classes** - the campaign-a-fix scorecard.
- **MTTR by severity** - and whether it’s trending down quarter over quarter.
- **Adoption of paved-road libraries** - % of HTTP calls through the wrapper, % of services using the auth helper.
- **High-risk project velocity** - pulled from the automated threat modeler in Leg 1. New projects flagged High this period, time from flag to mitigation, backlog awaiting human deep-dive. A leading indicator: are you shipping risky surface faster than you can secure it?
- **Cost per merged PR** - the single leading indicator that tells you whether your AI tooling is wired right. Stable as engineering velocity grows = architecture is right. Climbing linearly with PR count = AI is sitting in front of work that should be behind it.

**Feedback loops, operationalized.** Every triage outcome: every Block override, every manual TP/FP confirmation, every FP justification comment feeds back into a central pattern store the AI reads on each invocation. Same FPs don’t surface twice. Signal quality improves month over month. If you find SSRF three times, the loop’s output isn’t three tickets; it’s an SSRF-safe HTTP client and a SAST rule blocking the unsafe primitive everywhere else.

**The AI meter: behind vs in front.** Every architecture decision has a cost shape. *Behind the meter:* deterministic SAST, custom rules, JSON pattern stores, CI compute. *In front of the meter:* AI per token, per call, per review.

*Push deterministic, high-volume work behind the meter. Reserve in-front spend for judgment.*

Wiring AI as the first line of defense means every PR pays the AI tax and the SAST engine sits idle behind it. The right architecture is the inverse:

**Working budget estimates** *(based on [current Anthropic API rates](https://claude.com/pricing), May 2026):*

- **Automated threat modeling:** ~$0.20–$1 per project on Sonnet. 40 projects/quarter ≈ $10–40/quarter. Cheapest line in your AI budget.
- **AI as FP filter on PRs:** ~$0.05 per finding on Sonnet. 10–100× that if AI runs on every PR regardless of SAST output.
- **AI contextual PR review:**`anthropics/claude-code-security-review` GitHub Action. ~$0.20–$0.40 per scan on Sonnet, ~$0.90–$1.80 on Opus. Roughly $25–$35/month on Sonnet at 30 PRs/week.
- **Full-repo AI pen test:**[Claude Security](https://support.claude.com/en/articles/14661296-use-claude-security) (Enterprise public beta). Direct token cost only, no platform fee. Roughly $15–$150 per scan for typical repos; large monorepos can run higher.
- **Weekly MCP metrics refresh:** ~$1–$2 per run on Sonnet. Under $20/month.

**Automated reporting via MCP.** Monthly business reviews, posture dashboards, KPI sparklines the work that gets cut first when the team is underwater. Solve it once: a weekly AI agent that pulls from three sources in your security stack through MCP (Model Context Protocol) connectors, *links data across them*, and refreshes a self-contained HTML dashboard.

**The cross-source layer is the actual value, not the dashboard.** Three separate dashboards: one for your scanner’s findings backlog, one for your GRC tool’s risk register, one for your IR tracker tell you “92 active Highs,” “5 unmitigated risks,” and “53 open IR projects.” That’s noise. What you actually want from a weekly read is the synthesized sentence:

*Your Platform team has 77 active findings of a single CWE class, corroborating an unmitigated tier-1 risk in your risk register, which maps to a compliance control whose test status contradicts the finding evidence. Three open IR projects fall in this category. Attack path: account takeover via stale tokens. Estimated cost: $2M–$10M.*

That’s a decision, not a metric. Getting from three lists of independent numbers to that sentence is what the cross-source layer doe,  and two config files do most of the work: a CWE-to-risk mapping that turns scanner findings into evidence on GRC risks, and an IR-project-to-risk mapping that turns incidents into evidence on the same risks. When evidence corroborates a risk whose compliance control still says *passing*, that’s a **contradicted control** — flagged prominently on the dashboard as the highest-priority thing to investigate.

**Other MCP sources feed standalone metrics alongside the cross-linked story.** The automated threat modeler from Leg 1 is the obvious one: its MCP exposes how many new projects were flagged High this period, time from flag to deep-dive, and the backlog of unreviewed High projects. That’s the *velocity* signal — are you shipping risky surface faster than you can secure it? Cross-linking isn’t required for it to be valuable; it shows up as its own KPI on the dashboard, with a sparkline showing the trend. The same goes for AI scan spend, paved-road adoption percentage, and bug bounty queue depth. Not every source needs to participate in cross-source linking to earn a place in the weekly read.

- **The weekly refresh agent** - pulls from MCPs, computes derived metrics, builds a single JSON data block, injects it between markers in the HTML, validates, and stops.*Cannot touch the renderer.*

**The non-negotiables that make it safe to automate:**

- **Markers, not regex.** Data lives between literal string markers in the HTML. If either marker is missing, the agent refuses to write. Fail loud beats silent corruption.
- **Validate before publishing.** File-size threshold, both`<script>` blocks pass syntax check, KPI totals non-zero. If validation fails, warn the user and skip the broadcast.
- **Never render false positives.** Hard rule. FP data dilutes every other number and erodes trust in the dashboard. If it shows a Critical, it’s a real Critical.
- **History is append-only.** That buffer powers every sparkline and ↑↓ delta on the dashboard. Never truncate it manually.

**Why MCP makes this tractable.** Before MCP, every source needed bespoke API integration glue that broke when vendors changed auth flows or response schemas. MCP turns your security stack into a uniform tool surface the agent can call without custom code. Onboarding a new scanner or GRC tool? Plug in its MCP, add one query to the agent prompt, and the dashboard picks it up on the next run.

The dashboard becomes the artifact your team points at in every leadership conversation, not “I think MTTR is improving,” but a live grade with a sparkline next to it, evidence chained from finding to risk to control to incident. Cheap to run (~$5/week), and the marginal cost of adding a new metric is one prompt edit.

**Developer feedback.** Your customers are engineers. If the tools are noisy or slow, they will be bypassed, quietly, then loudly. Measure adoption and satisfaction. A pre-commit hook that takes 90 seconds will be uninstalled by Friday.

**Cultural integration.** A healthy AppSec program feels invisible. Security shows up as an accelerator: safer code, faster delivery, not a last-minute blocker.

*AI is best when it does the work that scales linearly with the size of your org, so humans can do the work that scales with judgment.*

## Build Order

Order matters more than calendar. Adjust the cadence to your team size.

## Anti-Patterns: Seven Failures That Sink AppSec Programs

If you take only one thing from this piece, take this list. Every one is something I’ve seen, done, or had to undo.

1. **AI in front of the meter.** Wiring AI to scan everything before deterministic tools have filtered. Invoice scales linearly with PRs; signal/noise stays bad. → SAST first, AI as FP filter on findings only.
2. **Threat modeling everything at design.** Becoming the standardization gatekeeper. Engineers route around you; you hear about projects in postmortems. → Default modeling for coverage, deep-dive only on flagged-High projects.
3. **Find → triage → ticket → project-manage-one-vulnerability.** Each finding treated as its own work item; backlog grows faster than fixes ship. → Campaign a fix: build the paved-road library and block the unsafe primitive at the SAST layer.
4. **Promoting rules to blocking before the FP rate is known.** Developers learn to ignore your CI inside a quarter, and you never get the trust back. → Advisory mode for a full quarter, telemetry-driven promotion, never go straight to Block.
5. **Annual compliance training as security education.** Zero behavior change, zero retention. → Just-in-time education at the moment of the mistake.
6. **Rules without a paved road.** “Don’t use`eval` “ without providing the safe alternative. Creative workarounds appear within a week. → Build the safe primitive first, then write the rule blocking the unsafe one.
7. **No feedback loop on AI findings.** Same FPs every month; AI tax compounds; signal quality flat. → Route every triage outcome back into a pattern store the AI reads on each invocation.

## What This Guide Doesn’t Solve

Honesty matters more than completeness. A few things this framework leaves unsolved:

- **Making DAST actually blocking at scale.** Nobody has DAST as a first-class blocking CI step. If you’ve solved this, write the post.
- **The single-monorepo case.** Per-repo context files and diff-aware baselines assume polyrepo or selective monorepo. A 5M-line monorepo changes every assumption.
- **Scaling past ~100 engineers on a single security person.** Past a certain headcount you need more humans. No amount of AI replaces a second reviewer.
- **AI hallucinating a real vulnerability via the wrong reasoning chain.** The validation pipeline catches false positives. It does not catch the inverse — AI describing an exploit that turns out to be real via a*different* mechanism than described. Read the underlying code, not the AI’s summary.

If you’re early in your program and any of the above feel like the biggest blockers — start with the foundation anyway. The four legs make every one of these easier to tackle later.

## Bringing It Together

Four legs, in order of leverage:

1. **Security by Default** - guardrails and paved roads. Removes the failure modes that cause everything else.
2. **Reactionary Security** - rapid detection without friction, on the projects that warrant a human pass.
3. **Security by Design** - patterns and education that scale.
4. **Continuous Feedback & Metrics** - iteration and improvement, with AI as the mechanism that makes the loop continuous.

**Stop asking “how do we find more?” Start asking “what can we move from reactionary to default?”**

Find → triage → ticket is the wrong loop. **Build → default → enforce is the right one.**

Miss one leg and the chair wobbles. Nail all four and security stops being a ticket factory. It becomes a force multiplier for engineering speed, resilience, and trust.

## Further Reading

- **Saltzer & Schroeder,** ***The Protection of Information in Computer Systems*** **(1975)** - the original eight design principles. The entire field, in fifteen pages.
- **Ross Anderson,** ***Security Engineering*** **(3rd ed.)** - the modern synthesis.
- **OWASP ASVS** - the most useful standardized requirements list; pairs well with custom SAST rules.
- **Adam Shostack,** ***Threat Modeling: Designing for Security*** - for the deeper threat-modeling work in Leg 2.
- **The Semgrep + LLM hybrid pattern** - worth reading before you wire it up.
