-
Notifications
You must be signed in to change notification settings - Fork 0
/
convert-action.js
241 lines (220 loc) · 8.71 KB
/
convert-action.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
const core = require('@actions/core');
//onst github = require('@actions/github');
const fs = require('fs');
const pipelineInputFileName = core.getInput('pipeline-results-json'); // 'results.json'
const sarifOutputFileName = core.getInput('output-results-sarif'); // 'veracode-results.sarif'
const srcBasePath1 = core.getInput('source-base-path-1'); // base path for the source in the repository
const srcBasePath2 = core.getInput('source-base-path-2'); // base path for the source in the repository
const srcBasePath3 = core.getInput('source-base-path-3'); // base path for the source in the repository
const reportLevels = core.getInput('finding-rule-level');
const replacer = [];
const levels = {};
const setupSourceReplacement = (sub1,sub2,sub3) => {
if (sub1!=undefined && sub1.length>0) {
_parseReplacer(sub1);
if (sub2!=undefined && sub2.length>0) {
_parseReplacer(sub2);
if (sub3!=undefined && sub3.length>0) {
_parseReplacer(sub3);
}
}
}
}
const _parseReplacer = (input) => {
const values = input.split(':');
if (values.length!=2) {
throw new Error('source-base-path attrbute in wrong format. Please refer to the action documentation');
}
const regEx = RegExp(values[0]);
replacer.push({
regex: regEx,
value: values[1]
})
}
const sliceReportLevels = (requestedLevels) => {
try {
const split = requestedLevels.split(':');
if (split===undefined || split.length!=3){
throw new Error("'finding-rule-level' should have 3 integer values seporated with ':' and no white spaces");
}
let vl = 5;
let gl = 'error';
let split_loc = 0;
let split_value = parseInt(split[split_loc]);
while (vl>=0){
if (vl>=split_value){
levels[""+vl] = gl;
vl--;
} else {
split_loc++;
if (split_loc==3) {
return;
}
split_value = parseInt(split[split_loc]);
if (gl === 'error'){
gl = 'warning';
} else {
gl = 'note';
}
}
}
} catch (e){
console.log(e);
throw new Error("See documentation for valid valuse for 'finding-rule-level'");
}
}
// none,note,warning,error
const sevIntToStr = (sevInt => {
return levels[sevInt];
})
const addRuleToRules = (issue,rules) => {
if (rules.filter(ruleItem => ruleItem.id===issue.cwe_id).length>0) {
return null;
}
/*
{
"id": "no-unused-vars",
"shortDescription": {
"text": "disallow unused variables"
},
"helpUri": "https://eslint.org/docs/rules/no-unused-vars",
"properties": {
"category": "Variables"
}
}
*/
let rule = {
id: issue.cwe_id,
shortDescription: {
text: "CWE-"+issue.cwe_id+": "+issue.issue_type
},
helpUri: "https://cwe.mitre.org/data/definitions/"+issue.cwe_id+".html",
properties: {
category: issue.issue_type_id,
tags: [issue.issue_type_id]
},
defaultConfiguration: {
level: sevIntToStr(issue.severity)
}
}
return rule;
}
const getFilePath = (filePath) => {
let final = filePath;
replacer.forEach(element => {
if (element.regex.test(final)){
final = final.replace(element.regex,element.value);
}
});
return final;
}
/*
{
"Title": "java.sql.Statement.executeQuery",
"IssueId": "1016",
"GOB": "B",
"Severity": "4",
"IssueTypeId": "taint",
"IssueType": "Improper Neutralization of Special Elements used in an SQL Command (\u0027SQL Injection\u0027)",
"CWEId": "89",
"VCId": "89.005",
"DisplayText": "\u003cspan\u003eThis database query contains a SQL injection flaw. The call to java.sql.Statement.executeQuery() constructs a dynamic SQL query using a variable derived from untrusted input. An attacker could exploit this flaw to execute arbitrary SQL queries against the database. The first argument to executeQuery() contains tainted data from the variable sqlQuery. The tainted data originated from an earlier call to AnnotationVirtualController.vc_annotation_entry.\u003c/span\u003e \u003cspan\u003eAvoid dynamically constructing SQL queries. Instead, use parameterized prepared statements to prevent the database from interpreting the contents of bind variables as part of the query. Always validate untrusted input to ensure that it conforms to the expected format, using centralized data validation routines when possible.\u003c/span\u003e \u003cspan\u003eReferences: \u003ca href\u003d\"https://cwe.mitre.org/data/definitions/89.html\"\u003eCWE\u003c/a\u003e \u003ca href\u003d\"https://www.owasp.org/index.php/SQL_injection\"\u003eOWASP\u003c/a\u003e \u003ca href\u003d\"https://webappsec.pbworks.com/SQL-Injection\"\u003eWASC\u003c/a\u003e\u003c/span\u003e",
"Files": {
"SourceFile": {
"File": "com/veracode/verademo/controller/UserController.java",
"Line": "166",
"FunctionName": "processLogin",
"QualifiedFunctionName": "com.veracode.verademo.controller.UserController.processLogin",
"FunctionPrototype": "java.lang.String processLogin(java.lang.String, java.lang.String, java.lang.String, java.lang.String, org.springframework.ui.Model, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)",
"Scope": "com.veracode.verademo.controller.UserController"
}
},
"FlawMatch": {
"ProcedureHash": "844194490",
"PrototypeHash": "839857025",
"FlawHash": "3392777041",
"FlawHashCount": "1",
"FlawHashOrdinal": "1",
"CauseHash": "1176028798",
"CauseHashCount": "1",
"CauseHashOrdinal": "1"
}
},
*/
const convertPipelineResultFileToSarifFile = (inputFileName,outputFileName) => {
var results = {};
let rawdata = fs.readFileSync(inputFileName);
results = JSON.parse(rawdata);
console.log('Pipeline Scan results file found and parsed - validated JSON file');
//"scan_status": "SUCCESS"
if (results.scan_status==='SUCCESS') {
let issues = results.findings;
console.log('Issues count: '+issues.length);
let rules=[];
// convert to SARIF json
let sarifResults = issues.map(issue => {
// append rule to ruleset - if not already there
let rule = addRuleToRules(issue,rules);
if (rule!==null){
rules.push(rule);
}
// construct flaw location
const issueFileLocation = issue.files.source_file;
const filePath = getFilePath(issueFileLocation.file);
let location = {
physicalLocation: {
artifactLocation: {
uri: filePath
},
region: {
startLine: parseInt(issueFileLocation.line)
}
}
}
// get the severity number to name
let serStr = sevIntToStr(issue.severity);
// construct the issue
let resultItem = {
level: serStr,
message: {
text: issue.display_text,
},
locations: [location],
ruleId: issue.cwe_id
}
return resultItem;
})
// construct the full SARIF content
let sarifFileJSONContent = {
$schema : "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
version : "2.1.0",
runs : [
{
tool : {
driver : {
name : "Veracode Pipeline Scanner",
rules: rules
}
},
results: sarifResults
}
]
};
// save to file
fs.writeFileSync(outputFileName,JSON.stringify(sarifFileJSONContent));
console.log('SARIF file created: '+outputFileName);
}
}
try {
sliceReportLevels(reportLevels);
setupSourceReplacement(srcBasePath1,srcBasePath2,srcBasePath3);
convertPipelineResultFileToSarifFile(pipelineInputFileName,sarifOutputFileName);
} catch (error) {
core.setFailed(error.message);
}
module.exports = {
sevIntToStr: sevIntToStr,
sliceReportLevels: sliceReportLevels,
convertToSarif: convertPipelineResultFileToSarifFile,
setupSourceReplacement: setupSourceReplacement
}