In short

A PSIRT is the team that receives, triages, fixes and discloses vulnerabilities in the products a company ships; a CSIRT handles incidents in the company's own infrastructure. Build it on the six service areas of the FIRST PSIRT Services Framework, starting with a staffed intake, a written triage process, response targets per severity, and a way to publish advisories and CVE records. Choose a distributed, centralized or hybrid model to fit your product portfolio, become a CVE Numbering Authority once you ship enough products to need one, and track every case in a record with timestamps so you can show what happened and when.

  • Do publish one intake route (VDP and security.txt) and staff it; an unread mailbox is the most common PSIRT failure.
  • Do triage with a written qualification step, reproduction and a CVSS 4.0 vector, then prioritize with the decision table in /frameworks/vulnerability-prioritization/.
  • Do set response targets per severity, with a much shorter path when exploitation is reported, and measure every case against them.
  • Do become a CNA when you ship products regularly, and publish CVE records within the CNA rules' 72-hour limit after disclosure.
  • Don't run PSIRT and incident response as strangers: an exploited product vulnerability is both a PSIRT case and an incident.

Verify it

  1. Send a test report through every published channel (security.txt contact, web form, support) and time the acknowledgement.
  2. Run the SLA script over all open cases each week and review every overdue stage with its owner.
  3. Pick the last three disclosed vulnerabilities and check that each has a CVE record with CWE, affected and fixed versions, published within 72 hours of the advisory.
  4. Check that an exploited-vulnerability case opens an incident and starts the CRA clock check automatically.
  5. Review the FIRST service areas once a year and record which services you provide and which you deliberately do not.
Applies to
  • FIRST PSIRT Services Framework 1.1
  • CVE CNA Operational Rules 4.2.0 (effective 25 August 2026)
  • ISO/IEC 29147:2018 (disclosure) and 30111:2019 (handling)
  • Artifacts JSON Schema draft 2020-12 (validated with Ajv); Python 3.14 (tested)

Organizations that ship software or devices to customers, including SaaS providers, that need a repeatable way to handle vulnerability reports and publish fixes and advisories.

Learning goals and prerequisitesAbout 33 minutes: 10 to read, 3 on the code and 20 for the exercise

What you will learn

  • Explain what a PSIRT does and choose a distributed, centralized or hybrid model.
  • Design intake, triage and response targets, and track them per case.
  • Decide when to become a CNA and what the CNA rules 4.2.0 require.
  • Coordinate multi-vendor vulnerabilities with embargoes and a lead coordinator.

Before you start

Product security leads, AppSec engineers and engineering managers who set up or run vulnerability handling for shipped products

  • Know which products and versions your company supports.
  • Read the coordinated disclosure guide first.
  • For the artifacts: Python 3.11 or later; Node.js with Ajv to validate the schema.

Refresh first: Run coordinated vulnerability disclosure from report to advisory

Go to the guided practice

One vulnerability report through the PSIRT

Your companyFinderPSIRTProduct teamCVE ProgramCustomersReport via VDP orsecurity.txt1Acknowledgementand case ID2Qualified case toreproduce andscore2Reserve CVE ID3Fixed versionsfor every branch4Agreed disclosuredate4Advisory, CSAF and notification5Publish the CVE record5Trust boundaryUntrusted input1Step, described below
  1. ReportA finder reports through the VDP form or the security.txt contact. The report is untrusted until it is qualified and reproduced.
  2. Acknowledge and triageThe PSIRT acknowledges with a case ID and the next update date, qualifies the report and has the product team reproduce it and score a CVSS 4.0 vector.
  3. Reserve a CVE IDOnce there is reasonable evidence of a vulnerability that will be disclosed, the PSIRT, as a CNA or through one, reserves a CVE ID.
  4. Fix and agree the dateThe product team fixes every supported version; the PSIRT agrees the disclosure date with the finder.
  5. DiscloseThe PSIRT publishes the advisory and CSAF document, notifies customers and publishes the CVE record: the CNA rules say within 24 hours of disclosing the ID, and require it within 72.
The PSIRT owns the case from report to disclosure; engineering owns the fix. Exploitation at any point also opens an incident.

What does a PSIRT do, and how is it different from a CSIRT?

A product security incident response team handles vulnerabilities in the products and services a company ships: it receives reports, gets them triaged and fixed, and tells customers what to do. The FIRST PSIRT Services Framework puts the difference simply: an enterprise CSIRT focuses on the security of the organization's own systems and networks, while the PSIRT focuses on products. The two must work together, because an exploited product vulnerability is both a PSIRT case and, for a SaaS provider or its customers, an incident.

NIST SSDF task RV.1.3 asks for a policy on vulnerability disclosure and remediation and gives "have a PSIRT and processes in place" as an example. Small companies can run the PSIRT function with a part-time lead and an on-call rotation; what matters is that every report has an owner and a clock from the moment it arrives.

What services does the FIRST PSIRT Services Framework define?

The framework groups PSIRT work into six service areas, each with numbered services. Nobody provides all of them on day one. Use the list to decide what you provide now, what later, and what you leave to others, and write that down in the PSIRT charter.

What services does the FIRST PSIRT Services Framework define?
Service areaServices in the frameworkBuild first
1 Stakeholder ecosystem management1.1 internal stakeholders, 1.2 finder community engagement, 1.3 community and organizational engagement, 1.4 downstream stakeholders, 1.5 incident communications within the organization, 1.6 recognition for finders, 1.7 stakeholder metricsNamed contacts in engineering, legal, support and communications; a thank-you or hall-of-fame practice
2 Vulnerability discovery2.1 intake of vulnerability reporting, 2.2 identifying unreported vulnerabilities, 2.3 monitoring product component vulnerabilities, 2.4 identifying new vulnerabilities, 2.5 discovery metricsOne intake route and monitoring of third-party components in your SBOMs
3 Vulnerability triage and analysis3.1 qualification, 3.2 established finders, 3.3 reproductionA written qualification checklist and a reproduction environment
4 Remediation4.1 remedy release management plan, 4.2 remediation, 4.3 incident handling, 4.4 release metricsFix targets per severity and a list of supported versions
5 Vulnerability disclosure5.1 notification, 5.2 coordination, 5.3 disclosure, 5.4 vulnerability metricsAn advisory template and a CVE route
6 Training and education6.1 training the PSIRT, 6.2 training development teams, 6.3 training validation teams, 6.4 continuing education, 6.5 feedback mechanismsFeed root causes into developer training; see /frameworks/developer-security-training/

Which organizational model fits your company?

The framework describes three models. Pick by portfolio and size, then document reporting lines and authority in the charter, because a PSIRT that cannot make product teams act will miss its targets.

Which organizational model fits your company?
ModelHow it worksFits when
DistributedA small core PSIRT works with representatives in each product team, who do most triage and remediationA large, diverse portfolio; the cost spreads across product teams and scales with them
CentralizedA larger PSIRT staff, drawn from several departments, reports to executives responsible for product securityFew products, high regulatory or customer pressure, or teams without security depth
HybridCentral intake, coordination and disclosure; product teams own triage depth and fixesMost mid-size companies; often the result of growing out of a distributed model

Who does what in a PSIRT case?

Write the roles down before the first case. The RACI below is a starting point; R does the work, A is accountable, C is consulted, I is informed. Security champions, described in /frameworks/security-champions-program/, are natural product-team contacts.

Who does what in a PSIRT case?
ActivityPSIRT leadProduct teamLegal and communicationsSupportExecutive sponsor
Acknowledge and qualify the reportA/RCII-
Reproduce and scoreAR---
Fix all supported versionsCA/R-I-
Agree the disclosure date with the finderA/RCC-I
CVE record and advisoryA/RCCI-
Customer notificationACRRI
Regulatory reporting (for example CRA)RCA-I

How should vulnerability reports reach you?

Publish one documented route and accept reports wherever they land. The VDP and security.txt are the front door; see /domains/appsec/vulnerability-response/vdp-bug-bounty-pentest/ for both. Support staff need a rule to forward anything that looks like a vulnerability report to the PSIRT queue the same day, and the PSIRT needs to watch upstream advisories for the components in its SBOMs, which the framework calls monitoring product component vulnerabilities.

Acknowledge every report with a case ID and the next update date, even before triage. The ticket should record the source, because the framework treats finders who report often (established finders) differently in qualification: their reports usually need less proof before reproduction starts.

  • External finders: VDP form or the security.txt contact; bug bounty platform if you run one.
  • Customers: through support, with a forwarding rule and a template that asks for version and impact.
  • Upstream suppliers and open-source projects: advisories for components in your SBOMs; see /domains/appsec/vulnerability-response/dependency-vulnerability-response/.
  • Coordinators such as CERT/CC, which runs the VINCE platform for multi-party cases, and national CSIRTs.
  • Internal sources: penetration tests, code review, scanners and incident investigations.

How do you triage a report?

Qualification decides whether the report is in scope, whether it describes a vulnerability in a supported product version, and whether it duplicates a known case. Reproduction confirms the behavior on a supported version with synthetic data, without widening exposure. Score a CVSS 4.0 vector for the advisory and the CVE record, and decide priority with exploitation, exposure and reachability as in /frameworks/vulnerability-prioritization/; a KEV listing or a report of exploitation in the wild moves the case to the exploited track at once.

Record a decision for every report, including "not a vulnerability" with the reason and "not affected" with evidence, which may become a VEX statement for customers. If the finder disagrees, follow the evidence-boundary approach in /domains/appsec/vulnerability-response/coordinated-disclosure/.

What response targets should a PSIRT set?

ISO/IEC 29147:2018 and 30111:2019 describe the disclosure and handling processes but leave the timings to you, so set targets, publish the ones that finders care about and measure every case. Outside expectations shape them: Google Project Zero gives vendors 90 days to make a fix available and 7 days for bugs exploited in the wild, and many researchers follow similar deadlines. The EU Cyber Resilience Act adds a 24-hour early warning for actively exploited vulnerabilities in products in scope, from 11 September 2026.

What response targets should a PSIRT set?
StageExample target (calendar days from receipt)Notes
Acknowledge2Automatic receipt is not acknowledgement; a person confirms the case and the next update date
Triage decision7Qualified, reproduced, scored, owner assigned
Fix released, critical30For all supported versions, or a documented mitigation
Fix released, high / medium / low60 / 90 / 180Low may ship in the next scheduled release if earlier
Exploited in the wild7 for the fix or mitigationAlso opens an incident; check CRA reporting the same day
CVE record after the advisoryWithin 24 hours (rule: must within 72)CNA rules 4.2.0, 4.5.1.3 and 4.5.1.4

What should a PSIRT case record contain?

Keep each case as structured data, whatever tracker you use, so you can report on it and so the same fields feed the advisory, the CVE record and the CSAF document. The schema below names the source, products with VEX-style statuses, the state, severity and vector, CWE, exploitation, the CVE, any embargo date and timestamps for each stage. It validates with Ajv 8 against JSON Schema draft 2020-12.

psirt-case.schema.jsonJSON
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://psirt.example/schemas/psirt-case.json",
  "title": "PSIRT case",
  "type": "object",
  "required": ["id", "source", "products", "state", "severity", "received", "owner"],
  "properties": {
    "id": {"type": "string", "pattern": "^PSIRT-[0-9]{4}-[0-9]{4}$"},
    "source": {"enum": ["vdp", "bug-bounty", "customer", "internal", "upstream", "coordinator", "researcher-public"]},
    "products": {
      "type": "array",
      "minItems": 1,
      "items": {
        "type": "object",
        "required": ["name", "versions", "status"],
        "properties": {
          "name": {"type": "string"},
          "versions": {"type": "string"},
          "status": {"enum": ["under_investigation", "known_affected", "known_not_affected", "fixed"]}
        }
      }
    },
    "state": {"enum": ["received", "acknowledged", "triaged", "fixing", "fix-released", "disclosed", "closed", "rejected"]},
    "severity": {"enum": ["critical", "high", "medium", "low", "none", "unknown"]},
    "cvss_vector": {"type": "string", "pattern": "^CVSS:(3\\.1|4\\.0)/"},
    "cwe": {"type": "array", "items": {"type": "string", "pattern": "^CWE-[0-9]+$"}},
    "exploited": {"type": "boolean", "description": "Reliable evidence of exploitation; starts CRA Article 14 clocks for in-scope products"},
    "cve": {"type": "string", "pattern": "^CVE-[0-9]{4}-[0-9]{4,}$"},
    "embargo_until": {"type": "string", "format": "date"},
    "owner": {"type": "string"},
    "received": {"type": "string", "format": "date-time"},
    "acknowledged": {"type": "string", "format": "date-time"},
    "triaged": {"type": "string", "format": "date-time"},
    "fix_released": {"type": "string", "format": "date-time"},
    "disclosed": {"type": "string", "format": "date-time"},
    "advisory": {"type": "string", "format": "uri"}
  },
  "additionalProperties": false
}

How do you check every case against its targets?

Run a small report every day or week over the case records. The script applies the example targets and marks each stage as met, missed, open with its due date, or overdue. On 10 October 2026, with three sample cases, it reported a critical case received on 1 September whose fix was overdue, a customer report received on 6 October whose acknowledgement was overdue, and an exploited high-severity case whose 7-day fix target had passed, with a reminder to check CRA reporting.

sla_status.pyPython
"""Report each PSIRT case against an example SLA policy (calendar days, local choices).

Usage: python sla_status.py cases.json 2026-10-10T12:00:00Z
"""
import json
import sys
from datetime import datetime, timedelta

POLICY = {  # target days from "received"; exploited cases use the "exploited" fix target
    "acknowledge": 2,
    "triage": 7,
    "fix": {"critical": 30, "high": 60, "medium": 90, "low": 180, "exploited": 7},
}


def ts(value):
    return datetime.fromisoformat(value.replace("Z", "+00:00"))


def status(case, now):
    received = ts(case["received"])
    checks = [("acknowledge", "acknowledged", POLICY["acknowledge"]), ("triage", "triaged", POLICY["triage"])]
    if case["severity"] in POLICY["fix"]:
        days = POLICY["fix"]["exploited"] if case.get("exploited") else POLICY["fix"][case["severity"]]
        checks.append(("fix", "fix_released", days))
    for stage, field, days in checks:
        due = received + timedelta(days=days)
        done = case.get(field)
        if done:
            state = "met" if ts(done) <= due else "missed"
        else:
            state = "OVERDUE" if now > due else f"open, due {due:%Y-%m-%d}"
        yield stage, state


if __name__ == "__main__":
    cases = json.load(open(sys.argv[1], encoding="utf-8"))
    now = ts(sys.argv[2])
    for case in cases:
        line = "; ".join(f"{stage} {state}" for stage, state in status(case, now))
        flag = " [exploited: check CRA Article 14 reporting]" if case.get("exploited") else ""
        print(f"{case['id']} {case['severity']:8} {line}{flag}")

Should you become a CVE Numbering Authority?

A CNA assigns CVE IDs and publishes CVE records for vulnerabilities within an agreed scope, usually its own products. Becoming one lets you publish authoritative records on your own schedule, with your own CWE, CVSS and version data, instead of waiting for another CNA. Under the CNA Operational Rules 4.2.0, effective 25 August 2026, a CNA should assign an ID when it has reasonable evidence of a vulnerability that is or will be publicly disclosed and is in its scope; must publish the record within 72 hours of disclosing the ID; and every record must name at least one affected product, a problem type (preferably a CWE), a description and a public reference. The rules also say a CNA must not use the type of technology, such as cloud or AI, as the only reason to decide whether to assign, and provide an exclusively-hosted-service tag for vulnerabilities that exist only in a hosted service.

To join, a CNA needs a scope definition, a public vulnerability disclosure policy and a public place where it publishes advisories, and it onboards through a Root. Organizations in Europe can now join under the ENISA Root. If you are not a CNA, ask the CNA whose scope covers your product: a vendor CNA, GitHub for open-source projects hosted there (GitHub is a CNA and assigns IDs for repository security advisories), or a CNA of last resort. See /domains/appsec/vulnerability-response/security-advisories-csaf-vex/ for publishing the record and the advisory.

How do you coordinate with other vendors?

Many vulnerabilities affect more than one vendor: a shared library, a protocol, or your component inside someone else's product. FIRST's "Guidelines and Practices for Multi-Party Vulnerability Coordination and Disclosure" (version 1.1, 2020) covers the common cases, from a vulnerability with no affected users to public disclosure or exploitation before the vendor knew. Its practical advice: give vendors a reasonable embargo to investigate and fix, agree timelines before disclosure and tell downstream vendors clearly, pick a single lead coordinator when several are involved, and set up upstream and downstream channels before you need them.

The PSIRT framework adds the intermediate-vendor case: if you supply components to other vendors, notify their PSIRTs of vulnerabilities in what you supply, and put timely notification into your contracts. For your own suppliers, ask for the same.

What should a PSIRT measure?

  • Time to acknowledge, triage and fix by severity, as medians and as the share of cases within target.
  • Cases overdue at each stage today, with owners.
  • Share of vulnerabilities fixed before public disclosure, and cases disclosed by others first.
  • Advisory quality: revisions caused by wrong version ranges or missing products.
  • Root causes by CWE, fed to developer training and to the vulnerability classes the company wants to eliminate.
  • Finder experience: repeat reporters and complaints about silence, from the case history.

Guided practice

Kestrel, a fictional company with a SaaS console, an on-premises agent sold in the EU and an open-source SDK on GitHub, wants to formalize its PSIRT. Use the cases and facts below to make three decisions and fix the backlog.

Supplied scenario material
Plain text
Kestrel (fictional): 18 product teams, 1 security engineer, champions in 6 teams.
Current intake: security@kestrel.example, read by the security engineer "when possible"; no security.txt.
Open cases on 2026-10-10:
  K-1 reported 2026-08-20 by a researcher: auth bypass in agent 5.x (critical); acknowledged 2026-08-29; fix in progress
  K-2 reported 2026-10-07 via support: SSRF in console import (high); no acknowledgement yet
  K-3 reported 2026-10-08 by CERT/CC: a vulnerability in a compression library used by agent and SDK; three other vendors affected; embargo proposed until 2026-11-04
  K-4 reported 2026-10-09: customer says K-1 is being exploited against their agent
CVEs so far: requested from MITRE as CNA of last resort; two took more than a month.
  1. 1. Pick the model and fix intake

    Choose an organizational model and the first two intake changes.

    Expected result and reasoning

    Hybrid: central intake and disclosure with one security engineer as PSIRT lead, product-team champions as triage contacts. Publish security.txt and a VDP pointing to a monitored queue with an on-call rotation, and add a support forwarding rule.

  2. 2. Apply the targets

    Using the example targets, state the status of K-1 and K-2.

    Expected result and reasoning

    K-1: acknowledgement took 9 days (target 2, missed); the 30-day critical fix target fell on 2026-09-19, so the fix is overdue. K-2: received 2026-10-07, acknowledgement due 2026-10-09, overdue; triage due 2026-10-14.

  3. 3. Handle the exploitation report

    What changes for K-1 after K-4?

    Expected result and reasoning

    K-1 moves to the exploited track: open an incident, ship a fix or mitigation within days, notify customers, and because the agent is sold in the EU, check CRA Article 14 reporting with a 24-hour early warning from becoming aware of exploitation.

  4. 4. Coordinate K-3

    How should Kestrel handle the multi-vendor case?

    Expected result and reasoning

    Accept CERT/CC as lead coordinator, keep the embargo unless exploitation appears, fix both agent and SDK, prepare advisories for 2026-11-04, and notify downstream customers that embed the SDK according to the agreed timeline.

  5. 5. Decide on the CNA question

    Should Kestrel become a CNA?

    Expected result and reasoning

    Yes, with scope covering its products, since delays through the CNA of last resort are slowing advisories; it needs a public VDP and advisory location first. Meanwhile, the SDK can get CVE IDs through GitHub repository security advisories, because GitHub is the CNA for projects it hosts unless another CNA covers them.

Your deliverable: A one-page PSIRT charter draft for Kestrel: model, intake routes, targets, the status of K-1 to K-4 with next actions, and the CNA decision.

Kestrel and its cases are fictional; the targets are examples, not values from FIRST, ISO or the CVE Program. Confirm CRA scope and reporting details for real products with the runbook and legal counsel.

Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.

No draft saved on this device yet.

Sources & further reading

The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.

  1. FIRST: PSIRT Services Framework 1.1
  2. FIRST: Guidelines and Practices for Multi-Party Vulnerability Coordination and Disclosure. Version 1.1 (Spring 2020).
  3. CVE Program: CNA Operational Rules 4.2.0. Effective 25 August 2026.
  4. CVE Program: CVE Numbering Authorities
  5. ENISA: Join the global CVE Program under the ENISA Root
  6. GitHub Docs: repository security advisories (GitHub as a CNA)
  7. CERT/CC: VINCE coordination platform
  8. CERT/CC Guide to Coordinated Vulnerability Disclosure
  9. Google Project Zero: vulnerability disclosure policy
  10. NIST SP 800-218: SSDF 1.1 (RV.1.3). Version 1.1 (February 2022).

Reference information

SDLC phase
Maintenance

Download this guide as Markdown