# Agent skills in security engineering: encoding the vulnerability triage loop

Working through the security queue, I keep hitting the same friction. A CVE lands with a CVSS 9.8 and a recommended library bump. Looks clear. Except: is this service actually internet-facing, or is it behind an internal proxy? Did we accept this risk last quarter and document it somewhere? Does the team that owned it six months ago still own it, or did they fold into a new squad? Is there already a ticket for the same thing in a different scanner? Is there a change freeze? Does the upgrade even qualify for the exception that's written in a wiki page nobody's looked at since the last reorg?

**The bottleneck isn't the CVE. It's reconstructing enough living context about the system, the team, and the org to decide what to do, and whether the scanner even named the right fix.**

This post is about how I'm encoding that loop as an agent skill: a scheduled run or a triggered agent that collects the context a security engineer would collect, so the engineer spends time on the disagreements rather than re-collecting the facts.

* * *

## The eight questions CVSS won't answer

A finding arrives with a name, a severity, a package, sometimes a suggested upgrade. The questions that decide whether anyone acts live somewhere else, and they go stale as the org changes: new microservices, new teams, a reorg, a scope change, a service that was internal last month and is now behind a public load balancer.

| Ambiguity | Why the scanner won't answer it |
| --- | --- |
| Exposure | Internal vs internet-facing, authenticated vs anonymous, network controls in front. The CVE doesn't know about your load balancer. |
| Data and criticality | What the system stores or processes, and whether it's on the critical path. CVSS doesn't read the data-classification sheet. |
| Existing treatment | Documented risk acceptance, compensating controls, WAF rules, segmentation. Scanners don't pick these up. |
| Duplicate | The same CVE in three tools, or the same service under two names. Closing one row does nothing to the others. |
| Change path | Can this be merged quickly, or does it need approval, a window, a freeze exception? |
| Exception | What's the written exception, and does *this* change qualify? |
| SLA | Target time-to-remediate for *this* class of asset, not the vendor's default. |
| Authority | Who can accept residual risk. Who owns the service *today*. |

FIRST's EPSS is explicit about this gap: it estimates the probability a CVE will be exploited in the next 30 days, and the user guide says it **should never be treated as a risk score**. Accessibility of the asset, the kind of weakness, the asset's purpose: those still have to come from you.

NIST SP 800-40 Rev. 4 makes the same point from the remediation side: patching is only one of four risk responses. Avoid (which includes taking the service off the internet) is also on the table. The planning for which systems are unpatchable, which can only be rebooted in a monthly window, and which maintenance group they belong to is organisational knowledge. It's not in the scanner.

* * *

## Using SSVC as the decision framework

Carnegie Mellon's SSVC, which CISA uses for its own prioritisation messaging, is a decision tree, not a score. The deployer tree maps four inputs (Exploitation, System Exposure, Automatable, Human Impact) to Defer, Scheduled, Out-of-Cycle, or Immediate.

System Exposure is Small, Controlled, or Open. Open means "internet or another widely accessible network where access cannot plausibly be restricted." The spec's default if you don't know: treat it as Open. That default is the whole argument for not letting an agent skip the cloud validation step.

SSVC doesn't invent owners, freeze calendars, or exception text. It tells you which questions, once answered, produce a decision that leadership already agreed to. The work is getting those answers from systems that disagree with each other, on Tuesday, after the reorg.

* * *

## The context loop I run on every alert

My job isn't "file a ticket." It's: understand the vulnerability, then collect the relevant context from memory and from Jira, GitHub, Slack, the security tools, observability, internal policy docs, last quarter's risk register, and synthesise a decision: what needs to be fixed, by when, by whom, and how.

That loop already has a trigger: a daily queue, a new tool alert, a high-risk vendor disclosure, a mailing list hit. It already has a write-back: the FAQ that keeps getting the same question, the risk register, the exception list. If those documents don't exist, every triage re-derives the same answers and they diverge.

Anthropic describes an agent skill as an onboarding guide for a new hire: procedural knowledge plus organisational context, loaded when it's relevant. That's the right frame. A queryable API over the scanners still returns a CVE. It doesn't tell you what you'd actually do with the row. The skill is what the agent is supposed to do once it can reach Jira and the cloud API.

* * *

## The triage skill - an abstract example

A triage skill isn't a prompt that says "be a senior security engineer." It's a list of questions that must be answered from a named source, with a rule for what happens when the source is missing.

```markdown
# Vulnerability triage

Do not assign this finding until every required field has a source.
If a source is missing, say so. Do not invent an owner.

## 1. Pull the finding
CVE / rule id, affected package or image, first-seen, scanner of record.

## 2. Identify the system
Service name, repo, cloud project, environment.
If the names disagree, stop and list the candidates.

## 3. Exposure (verify in cloud or code, not from ticket text)
Internet-facing? Authn in front? Network controls?
SSVC System Exposure: Small / Controlled / Open.
If unknown: Open.

## 4. Organisational context
Owner team *today*. Data class. Criticality.
Duplicate tickets. Prior risk acceptance or compensating control.
Change freeze. Written exception, and whether this change qualifies.
Who can accept residual risk. Target SLA for this asset class.

## 5. Decide the action
NIST response: accept / mitigate / transfer / avoid.
Intended fix in one sentence. If the scanner's fix and the exposure
fix disagree, prefer the exposure fix and say why.

## 6. Write back
Update the ticket. If this answers a recurring question, update the
FAQ or risk register. Do not leave the answer only in chat.
```

Step 3 is the critical one. An LLM that only sees the CVE will tell you to bump the library. That can be right. It can also be the wrong object. A service marked internal in a wiki and actually published through a load balancer is Open, whatever the wiki says. A tool that should never have been on the internet doesn't get a patch as the primary action; it gets taken off the internet. That's the NIST avoid response and SSVC's "reduce exposure." The agent has to look at the cloud resource, the ingress, the IAM, the repo's actual config. Ticket text is a rumour.

Triggers stay boring: a cron for the daily queue, a webhook from the scanner or the disclosure inbox. The output is the filled fields, the most likely next steps an engineer would take, and the gaps that still need a human.

* * *

## The triage agent and the progress agent

Once the finding is triaged and assigned, a different loop starts. Some intended fixes are a pull request. Some are a change that needs approval because the blast radius is small but not zero, and past cases say so. Some are "page the on-call," which is a PagerDuty action, not a Jira comment.

```plaintext
alert / disclosure / daily cron
        |
        v
[ triage skill ] -- tools --> scanners, Jira, GitHub, cloud, policy docs
        |
        +--> ticket: owner, SLA, intended fix, gaps
        |
        +--> if the intended fix is bounded: PR or approval request
        +--> if the written rule says so: page
        |
        v
[ progress skill ] --> SLA drift, freeze, ownership change, duplicates
        |
        v
write-back: FAQ, risk register, exceptions
```

The bounded auto-fix path:

1.  The intended fix, from triage, is a code or config change with a known small blast radius.
    
2.  The agent opens a PR (or a change request) that cites the finding, the exposure check, and the SLA.
    
3.  CI and the usual reviewers still run. The agent doesn't merge to production.
    
4.  If the case matches a written "page immediately" rule (KEV plus Open exposure, or a disclosure that is already being exploited), it pages. It doesn't wait for the daily standup.
    

The progress agent watches work that's already assigned. Tickets don't close themselves because someone reacted in Slack. It checks: is the PR up, is the SLA slipping, did the freeze start after assignment, did ownership move, did a new duplicate land. It reports drift; it doesn't silently re-prioritise the queue unless the skill says a KEV-class finding can jump the line.

Write-back is part of both loops. Every novel exception, every "this service is internal despite the public IP we just removed," every FAQ that would otherwise be re-asked next week belongs in the reference docs the next run will read. Otherwise the skill is doing Slack archaeology on its own previous answers.

* * *

## Hard stops: what the agent must not invent

Do not invent an owner, a risk acceptance, a freeze exception, a duplicate without the other ticket id, or a production change that skipped the approval path the skill named.

Those are the failure modes that make this worse than an engineer with too many tabs. An invented owner assigns the wrong team and burns a week. A skipped freeze takes down a payment window. A hallucinated "already accepted" leaves an Open service sitting on a KEV. The skill's job is to make those refusals cheap: missing source, stop and say so.

The right calibration: replay last month's tickets against the skill. Did it match the owner, the exposure, the action the engineer actually took? If it keeps recommending library bumps on services that were taken off the internet, the skill is wrong, not the model.

* * *

## Dual tracks of Agentic security work

Agentic security work is arriving along two tracks that people keep collapsing into one. Track one is find-and-patch the code: automated discovery, patch generation, upstream contributions. Track two is run the security org: take an alert, gather living context, validate it against the cloud, decide, assign, maybe open a PR, then watch the work.

Track two is the one that dies without a knowledge base that's allowed to change. The org will keep changing: new services, new teams, new scope. The skill is the onboarding guide. The connectors are MCP or equivalent. The agent is the engineer who runs the guide on every new alert so the human spends time on the disagreements, not on re-collecting the facts.

The scanner found the CVE. That was the easy part.

* * *

## References

*   FIRST, *Exploit Prediction Scoring System (EPSS) User Guide*. https://www.first.org/epss/user-guide
    
*   CISA, *Known Exploited Vulnerabilities Catalog*. https://www.cisa.gov/known-exploited-vulnerabilities-catalog
    
*   CISA, *Stakeholder-Specific Vulnerability Categorization (SSVC)*. https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc
    
*   CERT/CC, *SSVC Deployer Decision Model*. https://certcc.github.io/SSVC/howto/deployer\_tree/
    
*   NIST SP 800-40 Rev. 4, *Guide to Enterprise Patch Management Planning*, April 2022. https://csrc.nist.gov/pubs/sp/800/40/r4/final
    
*   Anthropic, *Equipping agents for the real world with Agent Skills*, 16 October 2025. https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills
