ed API endpoint for vulnerability submissions. Require cryptographic signature verification to prevent spoofing and enforce payload schema validation.
import { createHmac, timingSafeEqual } from 'crypto';
import { z } from 'zod';
import { Router } from 'express';
const submissionSchema = z.object({
reporter: z.string().min(1),
contact: z.string().email(),
title: z.string().min(10),
severity: z.enum(['critical', 'high', 'medium', 'low', 'info']),
description: z.string().min(50),
reproduction_steps: z.string(),
affected_component: z.string(),
attachments: z.array(z.string().url()).max(5).optional(),
timestamp: z.string().datetime()
});
export function createDisclosureRouter(secret: string) {
const router = Router();
router.post('/v1/disclosure', async (req, res) => {
const signature = req.headers['x-disclosure-sig'] as string;
if (!signature || !verifySignature(req.rawBody, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const parseResult = submissionSchema.safeParse(req.body);
if (!parseResult.success) {
return res.status(422).json({ error: 'Invalid payload', details: parseResult.error.format() });
}
const report = parseResult.data;
// Route to triage pipeline
await triagePipeline.dispatch(report);
res.status(202).json({
id: generateReportId(),
status: 'acknowledged',
expected_response_window: '48h'
});
});
return router;
}
function verifySignature(payload: Buffer, signature: string, secret: string): boolean {
const hmac = createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
return timingSafeEqual(Buffer.from(signature), Buffer.from(`sha256=${digest}`));
}
Step 2: Automated Triage & Routing
Implement a severity-weighted routing engine that maps reports to appropriate engineering squads, applies duplicate detection, and assigns SLA timers.
interface TriageResult {
reportId: string;
severity: string;
assigned_team: string;
sla_hours: number;
duplicate_of?: string;
risk_score: number;
}
export class TriageEngine {
private readonly severityWeights = { critical: 4, high: 3, medium: 2, low: 1, info: 0 };
private readonly teamRouting: Record<string, string[]> = {
auth: ['oauth', 'jwt', 'session', 'mfa'],
data: ['database', 'api', 'graphql', 'query'],
infra: ['kubernetes', 'network', 'dns', 'cdn'],
frontend: ['xss', 'csrf', 'dom', 'client']
};
async evaluate(report: z.infer<typeof submissionSchema>): Promise<TriageResult> {
const baseScore = this.severityWeights[report.severity];
const componentMatch = Object.entries(this.teamRouting).find(([_, keywords]) =>
keywords.some(k => report.affected_component.toLowerCase().includes(k))
);
const assigned_team = componentMatch?.[0] ?? 'security-ops';
const slaHours = { critical: 4, high: 12, medium: 48, low: 168, info: 336 }[report.severity];
const duplicateCheck = await this.detectDuplicates(report.title, report.description);
return {
reportId: report.id,
severity: report.severity,
assigned_team,
sla_hours: slaHours,
duplicate_of: duplicateCheck?.id,
risk_score: baseScore + (duplicateCheck ? -1 : 0)
};
}
private async detectDuplicates(title: string, description: string) {
// Implement semantic similarity or keyword overlap against existing reports
// Returns { id: string } if match found, else undefined
return undefined;
}
}
Step 3: SLA Enforcement & Escalation
Track acknowledgment and resolution deadlines. Trigger automated reminders and escalate to leadership when thresholds are breached.
export class SLAMonitor {
async track(reportId: string, triage: TriageResult) {
const deadline = new Date(Date.now() + triage.sla_hours * 3600000);
await db.collection('sla_tracker').insertOne({
reportId,
deadline,
status: 'pending',
escalation_path: this.getEscalationPath(triage.severity)
});
setTimeout(() => this.checkDeadline(reportId), triage.sla_hours * 3600000);
}
private async checkDeadline(reportId: string) {
const record = await db.collection('sla_tracker').findOne({ reportId, status: 'pending' });
if (!record) return;
await notificationService.send({
to: record.escalation_path,
subject: `SLA Breach Warning: ${reportId}`,
body: `Vulnerability ${reportId} exceeds response window. Immediate triage required.`
});
await db.collection('sla_tracker').updateOne(
{ reportId },
{ $set: { status: 'breached', breached_at: new Date() } }
);
}
private getEscalationPath(severity: string): string[] {
const paths: Record<string, string[]> = {
critical: ['security-lead', 'vp-engineering', 'ciso'],
high: ['security-lead', 'engineering-manager'],
medium: ['security-ops'],
low: ['security-ops'],
info: ['security-ops']
};
return paths[severity] ?? ['security-ops'];
}
}
Architecture Decisions & Rationale
- Event-driven triage: Decouples intake from resolution. Enables horizontal scaling during high-volume disclosure periods (e.g., post-breach or public campaign).
- Deterministic routing over manual assignment: Reduces human bias and ensures consistent ownership. Keyword/component mapping aligns reports with domain expertise.
- Immutable audit logging: Every state change, communication, and SLA event is logged to a write-once store. Required for compliance (SOC 2, ISO 27001) and post-incident analysis.
- Safe harbor enforcement at intake: Policy acceptance is mandatory before report processing. Eliminates legal ambiguity and reduces public escalation risk.
- Integration hooks: Webhooks to Jira, GitHub, and SIEM ensure vulnerability data flows into existing engineering workflows without manual transcription.
Pitfall Guide
1. Treating All Reports Equally
Mistake: Applying identical response procedures to critical RCEs and informational UI inconsistencies.
Best Practice: Implement a severity-weighted triage matrix. Critical and high reports require immediate engineering engagement and executive visibility. Low and informational reports follow batched review cycles.
2. Omitting Safe Harbor Language
Mistake: Failing to explicitly state that good-faith security research will not trigger legal action.
Best Practice: Embed safe harbor terms in the submission form, auto-reply, and public policy page. Reference DMCA 1201 exemptions and CISA coordinated disclosure guidelines.
3. Over-Automating Triage Without Human Validation
Mistake: Relying solely on keyword matching or ML scoring to assign severity or close reports.
Best Practice: Use automation for routing and duplicate detection. Require security engineer sign-off before status changes to fixed, won't fix, or duplicate. Log all human overrides.
4. Public Disclosure Before Patch Availability
Mistake: Publishing vulnerability details or CVE IDs before a remediation path exists.
Best Practice: Enforce a coordination window. Disclose only after patch deployment, mitigation guidance is published, and affected users are notified. Align with ISO/IEC 29147 (Vulnerability Disclosure) and 30111 (Vulnerability Handling).
5. Missing Identifier Assignment Workflow
Mistake: Failing to request CVE IDs or internal tracking numbers, causing fragmentation across databases.
Best Practice: Integrate with a CNA (CVE Numbering Authority) or use a consistent internal ID scheme. Automate CNVD/MITRE requests upon triage approval. Maintain a mapping table between internal IDs, CVEs, and patch versions.
6. No Post-Mortem or Feedback Loop
Mistake: Closing reports without analyzing root cause or updating detection rules.
Best Practice: Mandate a 15-minute triage review for critical findings. Update WAF rules, SAST queries, and component inventories based on disclosed vulnerabilities. Track recurring patterns to drive architectural remediation.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Open Source Project | GitHub Security Advisories + README policy | Low overhead, leverages existing platform, community-friendly | Minimal |
| Enterprise SaaS | Coordinated Disclosure Pipeline + CDP integration | Requires audit trails, SLA enforcement, and compliance reporting | Moderate (engineering + tooling) |
| IoT/Hardware | Structured email + PGP encryption + dedicated triage team | Air-gapped systems, longer patch cycles, legal risk mitigation | High (specialized staffing) |
| Regulated Industry (FinTech/Health) | Formal CDP with legal review gate + immutable audit log | HIPAA/SOC2/PCI-DSS requirements mandate controlled disclosure | High (compliance overhead) |
Configuration Template
# disclosure-config.yaml
program:
name: "Acme Coordinated Disclosure"
version: "2.1"
safe_harbor: true
legal_contact: "security-legal@acme.io"
intake:
endpoint: "https://security.acme.io/v1/disclosure"
signature_algorithm: "HMAC-SHA256"
rate_limit: "100/hour"
max_attachment_size_mb: 10
triage:
severity_weights:
critical: 4
high: 3
medium: 2
low: 1
info: 0
routing_rules:
- component_keywords: ["auth", "oauth", "jwt"]
team: "identity-platform"
- component_keywords: ["database", "api", "graphql"]
team: "data-engineering"
- component_keywords: ["kubernetes", "network", "dns"]
team: "infra-ops"
duplicate_detection:
enabled: true
similarity_threshold: 0.85
sla:
acknowledgment:
critical: 4h
high: 12h
medium: 48h
low: 168h
resolution_target:
critical: 7d
high: 14d
medium: 30d
low: 90d
escalation:
breach_notification: true
channels: ["pagerduty", "slack-security", "email"]
disclosure:
coordination_window_days: 90
cna_integration: true
public_advisory_template: "advisory-template.md"
embargo_policy: "strict"
audit:
log_destination: "s3://acme-security-audit/disclosure/"
retention_days: 2555
immutability: true
Quick Start Guide
- Deploy the intake endpoint: Clone the disclosure router template, configure your HMAC secret, and expose it behind a WAF with rate limiting. Validate payload schema using the provided Zod schema.
- Publish your policy: Host a
security.txt file at /.well-known/security.txt pointing to your intake endpoint. Add safe harbor language, PGP public key, and response expectations.
- Configure routing & SLAs: Load the YAML configuration into your triage engine. Map component keywords to your engineering teams. Set SLA timers aligned with your risk tolerance.
- Instrument monitoring: Connect triage events to your observability stack. Track MTTA, MTTP, and SLA breach rates. Set alerts for critical reports exceeding acknowledgment thresholds.
- Validate with a test report: Submit a controlled, non-destructive test vulnerability. Verify signature validation, triage routing, SLA timer creation, and audit logging. Adjust thresholds based on observed latency.
A disciplined vulnerability disclosure process transforms reactive chaos into predictable incident response. By engineering the intake pipeline, enforcing deterministic triage, and maintaining coordination discipline, organizations compress exploitation windows, reduce legal exposure, and build trust with the security research community.