@vulcn/plugin-report 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -65,18 +65,137 @@ declare function generateJson(session: Session, result: RunResult, generatedAt:
65
65
 
66
66
  declare function generateYaml(session: Session, result: RunResult, generatedAt: string, engineVersion: string): string;
67
67
 
68
+ /**
69
+ * SARIF Report Generator for Vulcn
70
+ *
71
+ * Produces SARIF v2.1.0 (Static Analysis Results Interchange Format)
72
+ * compatible with GitHub Code Scanning, Azure DevOps, and other
73
+ * SARIF-consuming tools.
74
+ *
75
+ * @see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
76
+ * @see https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning
77
+ */
78
+
79
+ /**
80
+ * SARIF v2.1.0 Log — top-level structure
81
+ */
82
+ interface SarifLog {
83
+ $schema: string;
84
+ version: "2.1.0";
85
+ runs: SarifRun[];
86
+ }
87
+ interface SarifRun {
88
+ tool: SarifTool;
89
+ results: SarifResult[];
90
+ invocations: SarifInvocation[];
91
+ artifacts?: SarifArtifact[];
92
+ }
93
+ interface SarifTool {
94
+ driver: SarifToolComponent;
95
+ }
96
+ interface SarifToolComponent {
97
+ name: string;
98
+ version: string;
99
+ informationUri: string;
100
+ semanticVersion: string;
101
+ rules: SarifRule[];
102
+ }
103
+ interface SarifRule {
104
+ id: string;
105
+ name: string;
106
+ shortDescription: {
107
+ text: string;
108
+ };
109
+ fullDescription: {
110
+ text: string;
111
+ };
112
+ helpUri: string;
113
+ help: {
114
+ text: string;
115
+ markdown: string;
116
+ };
117
+ properties: {
118
+ tags: string[];
119
+ precision: "very-high" | "high" | "medium" | "low";
120
+ "security-severity": string;
121
+ };
122
+ defaultConfiguration: {
123
+ level: SarifLevel;
124
+ };
125
+ }
126
+ type SarifLevel = "error" | "warning" | "note" | "none";
127
+ interface SarifResult {
128
+ ruleId: string;
129
+ ruleIndex: number;
130
+ level: SarifLevel;
131
+ message: {
132
+ text: string;
133
+ };
134
+ locations: SarifLocation[];
135
+ fingerprints: Record<string, string>;
136
+ partialFingerprints: Record<string, string>;
137
+ properties: Record<string, unknown>;
138
+ }
139
+ interface SarifLocation {
140
+ physicalLocation: {
141
+ artifactLocation: {
142
+ uri: string;
143
+ uriBaseId?: string;
144
+ };
145
+ region?: {
146
+ startLine: number;
147
+ startColumn?: number;
148
+ };
149
+ };
150
+ logicalLocations?: Array<{
151
+ name: string;
152
+ kind: string;
153
+ }>;
154
+ }
155
+ interface SarifInvocation {
156
+ executionSuccessful: boolean;
157
+ startTimeUtc?: string;
158
+ endTimeUtc?: string;
159
+ properties?: Record<string, unknown>;
160
+ }
161
+ interface SarifArtifact {
162
+ location: {
163
+ uri: string;
164
+ };
165
+ length?: number;
166
+ }
167
+ /**
168
+ * Generate a SARIF v2.1.0 log from Vulcn scan results.
169
+ *
170
+ * Usage:
171
+ * const sarif = generateSarif(session, result, generatedAt, "0.4.0");
172
+ * await writeFile("vulcn-report.sarif", JSON.stringify(sarif, null, 2));
173
+ *
174
+ * The output can be uploaded to:
175
+ * - GitHub Code Scanning: `gh api /repos/{owner}/{repo}/code-scanning/sarifs`
176
+ * - GitHub Actions: `github/codeql-action/upload-sarif@v3`
177
+ * - Azure DevOps: SARIF SAST Scans Tab extension
178
+ *
179
+ * @param session - The session that was executed
180
+ * @param result - The run result with findings
181
+ * @param generatedAt - ISO timestamp
182
+ * @param engineVersion - Vulcn engine version
183
+ */
184
+ declare function generateSarif(session: Session, result: RunResult, generatedAt: string, engineVersion: string): SarifLog;
185
+
68
186
  /**
69
187
  * @vulcn/plugin-report
70
188
  * Report Generation Plugin for Vulcn
71
189
  *
72
- * Generates security reports in HTML, JSON, and YAML formats
190
+ * Generates security reports in HTML, JSON, YAML, and SARIF formats
73
191
  * after a run completes. Features:
74
192
  * - Modern dark-themed HTML report with Vulcn branding
75
193
  * - Machine-readable JSON for CI/CD integration
76
194
  * - Human-readable YAML for documentation
195
+ * - SARIF v2.1.0 for GitHub Code Scanning and IDE integration
77
196
  *
78
197
  * Configuration:
79
- * format: "html" | "json" | "yaml" | "all" (default: "html")
198
+ * format: "html" | "json" | "yaml" | "sarif" | "all" (default: "html")
80
199
  * outputDir: directory for reports (default: ".")
81
200
  * filename: base filename (no extension) (default: "vulcn-report")
82
201
  * open: auto-open HTML in browser (default: false)
@@ -91,10 +210,11 @@ declare const configSchema: z.ZodObject<{
91
210
  * - "html": Beautiful dark-themed HTML report
92
211
  * - "json": Machine-readable structured JSON
93
212
  * - "yaml": Human-readable YAML
94
- * - "all": Generate all three formats
213
+ * - "sarif": SARIF v2.1.0 for GitHub Code Scanning
214
+ * - "all": Generate all formats
95
215
  * @default "html"
96
216
  */
97
- format: z.ZodDefault<z.ZodEnum<["html", "json", "yaml", "all"]>>;
217
+ format: z.ZodDefault<z.ZodEnum<["html", "json", "yaml", "sarif", "all"]>>;
98
218
  /**
99
219
  * Output directory for report files
100
220
  * @default "."
@@ -111,12 +231,12 @@ declare const configSchema: z.ZodObject<{
111
231
  */
112
232
  open: z.ZodDefault<z.ZodBoolean>;
113
233
  }, "strip", z.ZodTypeAny, {
114
- format: "html" | "json" | "yaml" | "all";
234
+ format: "html" | "json" | "yaml" | "sarif" | "all";
115
235
  outputDir: string;
116
236
  filename: string;
117
237
  open: boolean;
118
238
  }, {
119
- format?: "html" | "json" | "yaml" | "all" | undefined;
239
+ format?: "html" | "json" | "yaml" | "sarif" | "all" | undefined;
120
240
  outputDir?: string | undefined;
121
241
  filename?: string | undefined;
122
242
  open?: boolean | undefined;
@@ -127,4 +247,4 @@ type ReportConfig = z.infer<typeof configSchema>;
127
247
  */
128
248
  declare const plugin: VulcnPlugin;
129
249
 
130
- export { type HtmlReportData, type JsonReport, type ReportConfig, configSchema, plugin as default, generateHtml, generateJson, generateYaml };
250
+ export { type HtmlReportData, type JsonReport, type ReportConfig, type SarifLog, configSchema, plugin as default, generateHtml, generateJson, generateSarif, generateYaml };
package/dist/index.d.ts CHANGED
@@ -65,18 +65,137 @@ declare function generateJson(session: Session, result: RunResult, generatedAt:
65
65
 
66
66
  declare function generateYaml(session: Session, result: RunResult, generatedAt: string, engineVersion: string): string;
67
67
 
68
+ /**
69
+ * SARIF Report Generator for Vulcn
70
+ *
71
+ * Produces SARIF v2.1.0 (Static Analysis Results Interchange Format)
72
+ * compatible with GitHub Code Scanning, Azure DevOps, and other
73
+ * SARIF-consuming tools.
74
+ *
75
+ * @see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
76
+ * @see https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning
77
+ */
78
+
79
+ /**
80
+ * SARIF v2.1.0 Log — top-level structure
81
+ */
82
+ interface SarifLog {
83
+ $schema: string;
84
+ version: "2.1.0";
85
+ runs: SarifRun[];
86
+ }
87
+ interface SarifRun {
88
+ tool: SarifTool;
89
+ results: SarifResult[];
90
+ invocations: SarifInvocation[];
91
+ artifacts?: SarifArtifact[];
92
+ }
93
+ interface SarifTool {
94
+ driver: SarifToolComponent;
95
+ }
96
+ interface SarifToolComponent {
97
+ name: string;
98
+ version: string;
99
+ informationUri: string;
100
+ semanticVersion: string;
101
+ rules: SarifRule[];
102
+ }
103
+ interface SarifRule {
104
+ id: string;
105
+ name: string;
106
+ shortDescription: {
107
+ text: string;
108
+ };
109
+ fullDescription: {
110
+ text: string;
111
+ };
112
+ helpUri: string;
113
+ help: {
114
+ text: string;
115
+ markdown: string;
116
+ };
117
+ properties: {
118
+ tags: string[];
119
+ precision: "very-high" | "high" | "medium" | "low";
120
+ "security-severity": string;
121
+ };
122
+ defaultConfiguration: {
123
+ level: SarifLevel;
124
+ };
125
+ }
126
+ type SarifLevel = "error" | "warning" | "note" | "none";
127
+ interface SarifResult {
128
+ ruleId: string;
129
+ ruleIndex: number;
130
+ level: SarifLevel;
131
+ message: {
132
+ text: string;
133
+ };
134
+ locations: SarifLocation[];
135
+ fingerprints: Record<string, string>;
136
+ partialFingerprints: Record<string, string>;
137
+ properties: Record<string, unknown>;
138
+ }
139
+ interface SarifLocation {
140
+ physicalLocation: {
141
+ artifactLocation: {
142
+ uri: string;
143
+ uriBaseId?: string;
144
+ };
145
+ region?: {
146
+ startLine: number;
147
+ startColumn?: number;
148
+ };
149
+ };
150
+ logicalLocations?: Array<{
151
+ name: string;
152
+ kind: string;
153
+ }>;
154
+ }
155
+ interface SarifInvocation {
156
+ executionSuccessful: boolean;
157
+ startTimeUtc?: string;
158
+ endTimeUtc?: string;
159
+ properties?: Record<string, unknown>;
160
+ }
161
+ interface SarifArtifact {
162
+ location: {
163
+ uri: string;
164
+ };
165
+ length?: number;
166
+ }
167
+ /**
168
+ * Generate a SARIF v2.1.0 log from Vulcn scan results.
169
+ *
170
+ * Usage:
171
+ * const sarif = generateSarif(session, result, generatedAt, "0.4.0");
172
+ * await writeFile("vulcn-report.sarif", JSON.stringify(sarif, null, 2));
173
+ *
174
+ * The output can be uploaded to:
175
+ * - GitHub Code Scanning: `gh api /repos/{owner}/{repo}/code-scanning/sarifs`
176
+ * - GitHub Actions: `github/codeql-action/upload-sarif@v3`
177
+ * - Azure DevOps: SARIF SAST Scans Tab extension
178
+ *
179
+ * @param session - The session that was executed
180
+ * @param result - The run result with findings
181
+ * @param generatedAt - ISO timestamp
182
+ * @param engineVersion - Vulcn engine version
183
+ */
184
+ declare function generateSarif(session: Session, result: RunResult, generatedAt: string, engineVersion: string): SarifLog;
185
+
68
186
  /**
69
187
  * @vulcn/plugin-report
70
188
  * Report Generation Plugin for Vulcn
71
189
  *
72
- * Generates security reports in HTML, JSON, and YAML formats
190
+ * Generates security reports in HTML, JSON, YAML, and SARIF formats
73
191
  * after a run completes. Features:
74
192
  * - Modern dark-themed HTML report with Vulcn branding
75
193
  * - Machine-readable JSON for CI/CD integration
76
194
  * - Human-readable YAML for documentation
195
+ * - SARIF v2.1.0 for GitHub Code Scanning and IDE integration
77
196
  *
78
197
  * Configuration:
79
- * format: "html" | "json" | "yaml" | "all" (default: "html")
198
+ * format: "html" | "json" | "yaml" | "sarif" | "all" (default: "html")
80
199
  * outputDir: directory for reports (default: ".")
81
200
  * filename: base filename (no extension) (default: "vulcn-report")
82
201
  * open: auto-open HTML in browser (default: false)
@@ -91,10 +210,11 @@ declare const configSchema: z.ZodObject<{
91
210
  * - "html": Beautiful dark-themed HTML report
92
211
  * - "json": Machine-readable structured JSON
93
212
  * - "yaml": Human-readable YAML
94
- * - "all": Generate all three formats
213
+ * - "sarif": SARIF v2.1.0 for GitHub Code Scanning
214
+ * - "all": Generate all formats
95
215
  * @default "html"
96
216
  */
97
- format: z.ZodDefault<z.ZodEnum<["html", "json", "yaml", "all"]>>;
217
+ format: z.ZodDefault<z.ZodEnum<["html", "json", "yaml", "sarif", "all"]>>;
98
218
  /**
99
219
  * Output directory for report files
100
220
  * @default "."
@@ -111,12 +231,12 @@ declare const configSchema: z.ZodObject<{
111
231
  */
112
232
  open: z.ZodDefault<z.ZodBoolean>;
113
233
  }, "strip", z.ZodTypeAny, {
114
- format: "html" | "json" | "yaml" | "all";
234
+ format: "html" | "json" | "yaml" | "sarif" | "all";
115
235
  outputDir: string;
116
236
  filename: string;
117
237
  open: boolean;
118
238
  }, {
119
- format?: "html" | "json" | "yaml" | "all" | undefined;
239
+ format?: "html" | "json" | "yaml" | "sarif" | "all" | undefined;
120
240
  outputDir?: string | undefined;
121
241
  filename?: string | undefined;
122
242
  open?: boolean | undefined;
@@ -127,4 +247,4 @@ type ReportConfig = z.infer<typeof configSchema>;
127
247
  */
128
248
  declare const plugin: VulcnPlugin;
129
249
 
130
- export { type HtmlReportData, type JsonReport, type ReportConfig, configSchema, plugin as default, generateHtml, generateJson, generateYaml };
250
+ export { type HtmlReportData, type JsonReport, type ReportConfig, type SarifLog, configSchema, plugin as default, generateHtml, generateJson, generateSarif, generateYaml };
package/dist/index.js CHANGED
@@ -928,6 +928,248 @@ function generateYaml(session, result, generatedAt, engineVersion) {
928
928
  return header + stringify(report, { indent: 2 });
929
929
  }
930
930
 
931
+ // src/sarif.ts
932
+ var CWE_MAP = {
933
+ xss: {
934
+ id: 79,
935
+ name: "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')"
936
+ },
937
+ sqli: {
938
+ id: 89,
939
+ name: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')"
940
+ },
941
+ ssrf: { id: 918, name: "Server-Side Request Forgery (SSRF)" },
942
+ xxe: {
943
+ id: 611,
944
+ name: "Improper Restriction of XML External Entity Reference"
945
+ },
946
+ "command-injection": {
947
+ id: 78,
948
+ name: "Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')"
949
+ },
950
+ "path-traversal": {
951
+ id: 22,
952
+ name: "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')"
953
+ },
954
+ "open-redirect": {
955
+ id: 601,
956
+ name: "URL Redirection to Untrusted Site ('Open Redirect')"
957
+ },
958
+ reflection: {
959
+ id: 200,
960
+ name: "Exposure of Sensitive Information to an Unauthorized Actor"
961
+ },
962
+ "security-misconfiguration": {
963
+ id: 16,
964
+ name: "Configuration"
965
+ },
966
+ "information-disclosure": {
967
+ id: 200,
968
+ name: "Exposure of Sensitive Information to an Unauthorized Actor"
969
+ },
970
+ custom: { id: 20, name: "Improper Input Validation" }
971
+ };
972
+ function toSarifLevel(severity) {
973
+ switch (severity) {
974
+ case "critical":
975
+ case "high":
976
+ return "error";
977
+ case "medium":
978
+ return "warning";
979
+ case "low":
980
+ case "info":
981
+ return "note";
982
+ default:
983
+ return "warning";
984
+ }
985
+ }
986
+ function toSecuritySeverity(severity) {
987
+ switch (severity) {
988
+ case "critical":
989
+ return "9.0";
990
+ case "high":
991
+ return "7.0";
992
+ case "medium":
993
+ return "4.0";
994
+ case "low":
995
+ return "2.0";
996
+ case "info":
997
+ return "0.0";
998
+ default:
999
+ return "4.0";
1000
+ }
1001
+ }
1002
+ function toPrecision(severity) {
1003
+ switch (severity) {
1004
+ case "critical":
1005
+ return "very-high";
1006
+ case "high":
1007
+ return "high";
1008
+ case "medium":
1009
+ return "medium";
1010
+ case "low":
1011
+ case "info":
1012
+ return "low";
1013
+ default:
1014
+ return "medium";
1015
+ }
1016
+ }
1017
+ function toRuleId(type) {
1018
+ return `VULCN-${type.toUpperCase().replace(/[^A-Z0-9]+/g, "-")}`;
1019
+ }
1020
+ function buildRules(findings) {
1021
+ const seenTypes = /* @__PURE__ */ new Map();
1022
+ for (const f of findings) {
1023
+ if (!seenTypes.has(f.type)) {
1024
+ seenTypes.set(f.type, f);
1025
+ }
1026
+ }
1027
+ return Array.from(seenTypes.entries()).map(([type, sampleFinding]) => {
1028
+ const cwe = CWE_MAP[type] || CWE_MAP.custom;
1029
+ const ruleId = toRuleId(type);
1030
+ return {
1031
+ id: ruleId,
1032
+ name: type,
1033
+ shortDescription: {
1034
+ text: `${cwe.name} (CWE-${cwe.id})`
1035
+ },
1036
+ fullDescription: {
1037
+ text: `Vulcn detected a potential ${type} vulnerability. ${cwe.name}. See CWE-${cwe.id} for details.`
1038
+ },
1039
+ helpUri: `https://cwe.mitre.org/data/definitions/${cwe.id}.html`,
1040
+ help: {
1041
+ text: `## ${cwe.name}
1042
+
1043
+ CWE-${cwe.id}: ${cwe.name}
1044
+
1045
+ This rule detects ${type} vulnerabilities by injecting security payloads into form inputs and analyzing the application's response for signs of exploitation.
1046
+
1047
+ ### Remediation
1048
+
1049
+ See https://cwe.mitre.org/data/definitions/${cwe.id}.html for detailed remediation guidance.`,
1050
+ markdown: `## ${cwe.name}
1051
+
1052
+ **CWE-${cwe.id}**: ${cwe.name}
1053
+
1054
+ This rule detects \`${type}\` vulnerabilities by injecting security payloads into form inputs and analyzing the application's response for signs of exploitation.
1055
+
1056
+ ### Remediation
1057
+
1058
+ See [CWE-${cwe.id}](https://cwe.mitre.org/data/definitions/${cwe.id}.html) for detailed remediation guidance.`
1059
+ },
1060
+ properties: {
1061
+ tags: ["security", `CWE-${cwe.id}`, `external/cwe/cwe-${cwe.id}`],
1062
+ precision: toPrecision(sampleFinding.severity),
1063
+ "security-severity": toSecuritySeverity(sampleFinding.severity)
1064
+ },
1065
+ defaultConfiguration: {
1066
+ level: toSarifLevel(sampleFinding.severity)
1067
+ }
1068
+ };
1069
+ });
1070
+ }
1071
+ function toSarifResult(finding, rules) {
1072
+ const ruleId = toRuleId(finding.type);
1073
+ const ruleIndex = rules.findIndex((r) => r.id === ruleId);
1074
+ let messageText = `${finding.title}
1075
+
1076
+ ${finding.description}`;
1077
+ if (finding.evidence) {
1078
+ messageText += `
1079
+
1080
+ Evidence: ${finding.evidence}`;
1081
+ }
1082
+ messageText += `
1083
+
1084
+ Payload: ${finding.payload}`;
1085
+ const uri = finding.url || "unknown";
1086
+ const fingerprint = `${finding.type}:${finding.stepId}:${finding.payload.slice(0, 50)}`;
1087
+ return {
1088
+ ruleId,
1089
+ ruleIndex: Math.max(ruleIndex, 0),
1090
+ level: toSarifLevel(finding.severity),
1091
+ message: { text: messageText },
1092
+ locations: [
1093
+ {
1094
+ physicalLocation: {
1095
+ artifactLocation: {
1096
+ uri
1097
+ },
1098
+ region: {
1099
+ startLine: 1
1100
+ }
1101
+ },
1102
+ logicalLocations: [
1103
+ {
1104
+ name: finding.stepId,
1105
+ kind: "test-step"
1106
+ }
1107
+ ]
1108
+ }
1109
+ ],
1110
+ fingerprints: {
1111
+ vulcnFindingV1: fingerprint
1112
+ },
1113
+ partialFingerprints: {
1114
+ vulcnType: finding.type,
1115
+ vulcnStepId: finding.stepId
1116
+ },
1117
+ properties: {
1118
+ severity: finding.severity,
1119
+ payload: finding.payload,
1120
+ stepId: finding.stepId,
1121
+ ...finding.evidence ? { evidence: finding.evidence } : {},
1122
+ ...finding.metadata || {}
1123
+ }
1124
+ };
1125
+ }
1126
+ function generateSarif(session, result, generatedAt, engineVersion) {
1127
+ const rules = buildRules(result.findings);
1128
+ const results = result.findings.map((f) => toSarifResult(f, rules));
1129
+ const uniqueUrls = [
1130
+ ...new Set(result.findings.map((f) => f.url).filter(Boolean))
1131
+ ];
1132
+ const artifacts = uniqueUrls.map((url) => ({
1133
+ location: { uri: url }
1134
+ }));
1135
+ const startDate = new Date(generatedAt);
1136
+ const endDate = new Date(startDate.getTime() + result.duration);
1137
+ const sarifLog = {
1138
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
1139
+ version: "2.1.0",
1140
+ runs: [
1141
+ {
1142
+ tool: {
1143
+ driver: {
1144
+ name: "Vulcn",
1145
+ version: engineVersion,
1146
+ semanticVersion: engineVersion,
1147
+ informationUri: "https://vulcn.dev",
1148
+ rules
1149
+ }
1150
+ },
1151
+ results,
1152
+ invocations: [
1153
+ {
1154
+ executionSuccessful: result.errors.length === 0,
1155
+ startTimeUtc: generatedAt,
1156
+ endTimeUtc: endDate.toISOString(),
1157
+ properties: {
1158
+ sessionName: session.name,
1159
+ stepsExecuted: result.stepsExecuted,
1160
+ payloadsTested: result.payloadsTested,
1161
+ durationMs: result.duration,
1162
+ ...result.errors.length > 0 ? { errors: result.errors } : {}
1163
+ }
1164
+ }
1165
+ ],
1166
+ ...artifacts.length > 0 ? { artifacts } : {}
1167
+ }
1168
+ ]
1169
+ };
1170
+ return sarifLog;
1171
+ }
1172
+
931
1173
  // src/index.ts
932
1174
  var configSchema = z.object({
933
1175
  /**
@@ -935,10 +1177,11 @@ var configSchema = z.object({
935
1177
  * - "html": Beautiful dark-themed HTML report
936
1178
  * - "json": Machine-readable structured JSON
937
1179
  * - "yaml": Human-readable YAML
938
- * - "all": Generate all three formats
1180
+ * - "sarif": SARIF v2.1.0 for GitHub Code Scanning
1181
+ * - "all": Generate all formats
939
1182
  * @default "html"
940
1183
  */
941
- format: z.enum(["html", "json", "yaml", "all"]).default("html"),
1184
+ format: z.enum(["html", "json", "yaml", "sarif", "all"]).default("html"),
942
1185
  /**
943
1186
  * Output directory for report files
944
1187
  * @default "."
@@ -956,14 +1199,14 @@ var configSchema = z.object({
956
1199
  open: z.boolean().default(false)
957
1200
  });
958
1201
  function getFormats(format) {
959
- if (format === "all") return ["html", "json", "yaml"];
1202
+ if (format === "all") return ["html", "json", "yaml", "sarif"];
960
1203
  return [format];
961
1204
  }
962
1205
  var plugin = {
963
1206
  name: "@vulcn/plugin-report",
964
1207
  version: "0.1.0",
965
1208
  apiVersion: 1,
966
- description: "Report generation plugin \u2014 generates beautiful HTML, JSON, and YAML security reports",
1209
+ description: "Report generation plugin \u2014 generates HTML, JSON, YAML, and SARIF security reports",
967
1210
  configSchema,
968
1211
  hooks: {
969
1212
  onInit: async (ctx) => {
@@ -1031,6 +1274,23 @@ var plugin = {
1031
1274
  ctx.logger.info(`\u{1F4C4} YAML report: ${yamlPath}`);
1032
1275
  break;
1033
1276
  }
1277
+ case "sarif": {
1278
+ const sarifReport = generateSarif(
1279
+ ctx.session,
1280
+ result,
1281
+ generatedAt,
1282
+ engineVersion
1283
+ );
1284
+ const sarifPath = `${basePath}.sarif`;
1285
+ await writeFile(
1286
+ sarifPath,
1287
+ JSON.stringify(sarifReport, null, 2),
1288
+ "utf-8"
1289
+ );
1290
+ writtenFiles.push(sarifPath);
1291
+ ctx.logger.info(`\u{1F4C4} SARIF report: ${sarifPath}`);
1292
+ break;
1293
+ }
1034
1294
  }
1035
1295
  } catch (err) {
1036
1296
  ctx.logger.error(
@@ -1057,6 +1317,7 @@ export {
1057
1317
  index_default as default,
1058
1318
  generateHtml,
1059
1319
  generateJson,
1320
+ generateSarif,
1060
1321
  generateYaml
1061
1322
  };
1062
1323
  //# sourceMappingURL=index.js.map