From 882633e6af64bcd7b2eb26e43c83c93f3a42f910 Mon Sep 17 00:00:00 2001 From: Al Francis Date: Mon, 25 May 2026 17:30:59 -0700 Subject: [PATCH 1/3] feat(scope): fill gaps in Scope Management module - Migration 0011: add allowed_vuln_types, severity_restriction, notes, exclusion_paths, deleted_at columns to scopes table - Schema: new fields typed in Drizzle schema - GET /api/admin/scopes: filter soft-deleted records - POST /api/admin/scopes: accept and persist new fields - PATCH /api/admin/scopes/[id]: update new fields - DELETE /api/admin/scopes/[id]: soft-delete (sets deleted_at) instead of hard delete - GET /api/scopes: filter deleted, expose new fields to submission form - Admin scope page: new fields in add/edit modal (vuln types, severity, notes, exclusion paths), archive dialog replaces delete dialog - Submit page: shows scope restrictions banner and filters vuln type / severity options when a target with restrictions is selected Co-Authored-By: Claude Sonnet 4.6 --- app/admin/scope/page.tsx | 126 +++++++++++++++++++++++-- app/api/admin/scopes/[id]/route.ts | 104 +++++++++----------- app/api/admin/scopes/route.ts | 52 +++++----- app/api/scopes/route.ts | 20 ++-- app/submit/page.tsx | 33 ++++++- lib/db/schema.ts | 21 +++-- migrations/0011_scope_enhancements.sql | 9 ++ 7 files changed, 259 insertions(+), 106 deletions(-) create mode 100644 migrations/0011_scope_enhancements.sql diff --git a/app/admin/scope/page.tsx b/app/admin/scope/page.tsx index ed5a1bc..e12f619 100644 --- a/app/admin/scope/page.tsx +++ b/app/admin/scope/page.tsx @@ -13,11 +13,27 @@ interface Scope { description: string | null; targetType: string; status: string; + allowedVulnTypes: string | null; // JSON array string + severityRestriction: string | null; // JSON array string + notes: string | null; + exclusionPaths: string | null; createdBy: string; createdAt: number; updatedAt: number; } +const ALL_VULN_TYPES = [ + 'Broken Access Control', 'Cryptographic Failure', 'Injection (SQL / XSS / Command / SSTI)', + 'Insecure Design', 'Security Misconfiguration', 'Vulnerable or Outdated Component', + 'Authentication / Session Failure', 'Software & Data Integrity Failure', + 'SSRF (Server-Side Request Forgery)', 'Business Logic Flaw', + 'Information Disclosure / Data Leak', 'IDOR (Insecure Direct Object Reference)', + 'Open Redirect', 'Clickjacking / UI Redressing', 'CORS Misconfiguration', + 'Path Traversal / File Inclusion', 'Other', +] as const; + +const ALL_SEVERITIES = ['Critical', 'High', 'Medium', 'Low', 'Info'] as const; + const TARGET_TYPE_LABELS: Record = { web_app: "Web Application", api: "API", @@ -42,6 +58,10 @@ export default function ScopeManagement() { description: "", targetType: "web_app", status: "active", + allowedVulnTypes: [] as string[], + severityRestriction: [] as string[], + notes: "", + exclusionPaths: "", }); const [toast, setToast] = useState<{ message: string; type: "success" | "error" | "info" } | null>(null); const [confirmDialog, setConfirmDialog] = useState<{ id: string; domain: string } | null>(null); @@ -158,7 +178,7 @@ export default function ScopeManagement() { await fetchScopes(); setShowAddModal(false); setEditingScope(null); - setFormData({ domain: "", description: "", targetType: "web_app", status: "active" }); + setFormData({ domain: "", description: "", targetType: "web_app", status: "active", allowedVulnTypes: [], severityRestriction: [], notes: "", exclusionPaths: "" }); setToast({ message: editingScope ? 'Scope updated successfully!' : 'Scope added successfully!', type: 'success' @@ -214,6 +234,10 @@ export default function ScopeManagement() { description: scope.description || "", targetType: scope.targetType, status: scope.status, + allowedVulnTypes: scope.allowedVulnTypes ? JSON.parse(scope.allowedVulnTypes) : [], + severityRestriction: scope.severityRestriction ? JSON.parse(scope.severityRestriction) : [], + notes: scope.notes || "", + exclusionPaths: scope.exclusionPaths || "", }); setShowAddModal(true); } @@ -221,7 +245,19 @@ export default function ScopeManagement() { function closeModal() { setShowAddModal(false); setEditingScope(null); - setFormData({ domain: "", description: "", targetType: "web_app", status: "active" }); + setFormData({ domain: "", description: "", targetType: "web_app", status: "active", allowedVulnTypes: [], severityRestriction: [], notes: "", exclusionPaths: "" }); + } + + function toggleArrayField(field: 'allowedVulnTypes' | 'severityRestriction', value: string) { + setFormData(prev => { + const current = prev[field]; + return { + ...prev, + [field]: current.includes(value) + ? current.filter(v => v !== value) + : [...current, value], + }; + }); } return ( @@ -402,7 +438,7 @@ export default function ScopeManagement() { {/* Add/Edit Modal */} {showAddModal && (
-
+

{editingScope ? 'Edit Target' : 'Add New Target'}

@@ -465,6 +501,82 @@ export default function ScopeManagement() {
+ {/* Allowed Vulnerability Types */} +
+ +
+ {ALL_VULN_TYPES.map(type => ( + + ))} +
+
+ + {/* Severity Restriction */} +
+ +
+ {ALL_SEVERITIES.map(sev => ( + + ))} +
+
+ + {/* Notes */} +
+ +