@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.cjs +266 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -7
- package/dist/index.d.ts +127 -7
- package/dist/index.js +265 -4
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
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
|
|
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
|
-
* - "
|
|
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
|
|
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
|
-
* - "
|
|
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
|
-
* - "
|
|
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
|
|
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
|