@mitre/hdf-converters 2.11.5 → 2.12.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/lib/index.d.ts +1 -1
- package/lib/index.js +2 -2
- package/lib/package.json +5 -6
- package/lib/src/base-converter.js +14 -3
- package/lib/src/base-converter.js.map +1 -1
- package/lib/src/ckl-mapper/checklist-jsonix-converter.js.map +1 -1
- package/lib/src/ckl-mapper/checklist-mapper.js.map +1 -1
- package/lib/src/mappings/OwaspNistMapping.d.ts +1 -1
- package/lib/src/mappings/OwaspNistMapping.js +30 -32
- package/lib/src/mappings/OwaspNistMapping.js.map +1 -1
- package/lib/src/mappings/{OWaspNistMappingData.js → OwaspNistMappingData.js} +1 -1
- package/lib/src/mappings/{OWaspNistMappingData.js.map → OwaspNistMappingData.js.map} +1 -1
- package/lib/src/sonarqube-mapper.d.ts +250 -42
- package/lib/src/sonarqube-mapper.js +503 -143
- package/lib/src/sonarqube-mapper.js.map +1 -1
- package/lib/src/xccdf-results-mapper.js +16 -18
- package/lib/src/xccdf-results-mapper.js.map +1 -1
- package/package.json +5 -6
- /package/lib/src/mappings/{OWaspNistMappingData.d.ts → OwaspNistMappingData.d.ts} +0 -0
|
@@ -1,16 +1,65 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
27
|
};
|
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
29
|
+
exports.SonarqubeResults = exports.SonarqubeMapper = void 0;
|
|
7
30
|
const axios_1 = __importDefault(require("axios"));
|
|
31
|
+
const _ = __importStar(require("lodash"));
|
|
32
|
+
const semver_1 = require("semver");
|
|
8
33
|
const inspecjs_1 = require("inspecjs");
|
|
9
34
|
const package_json_1 = require("../package.json");
|
|
10
35
|
const base_converter_1 = require("./base-converter");
|
|
11
36
|
const CweNistMapping_1 = require("./mappings/CweNistMapping");
|
|
12
37
|
const OwaspNistMapping_1 = require("./mappings/OwaspNistMapping");
|
|
13
38
|
const global_1 = require("./utils/global");
|
|
39
|
+
const logger = (0, global_1.createWinstonLogger)('SonarQube2HDF');
|
|
40
|
+
var SonarqubeVersion;
|
|
41
|
+
(function (SonarqubeVersion) {
|
|
42
|
+
SonarqubeVersion["Eight"] = "8.0.0";
|
|
43
|
+
SonarqubeVersion["Nine"] = "9.0.0";
|
|
44
|
+
SonarqubeVersion["Ten"] = "10.0.0";
|
|
45
|
+
SonarqubeVersion["Twenty_five"] = "25.0.0";
|
|
46
|
+
})(SonarqubeVersion || (SonarqubeVersion = {}));
|
|
47
|
+
function isSonarqubeVersionEight(version) {
|
|
48
|
+
const nextHigherVersion = SonarqubeVersion.Nine;
|
|
49
|
+
return (0, semver_1.lt)((0, semver_1.coerce)(version) || nextHigherVersion, nextHigherVersion);
|
|
50
|
+
}
|
|
51
|
+
function isSonarqubeVersionNine(version) {
|
|
52
|
+
const nextHigherVersion = SonarqubeVersion.Ten;
|
|
53
|
+
return (0, semver_1.lt)((0, semver_1.coerce)(version) || nextHigherVersion, nextHigherVersion);
|
|
54
|
+
}
|
|
55
|
+
function isSonarqubeVersionTen(version) {
|
|
56
|
+
const nextHigherVersion = SonarqubeVersion.Twenty_five;
|
|
57
|
+
return (0, semver_1.lt)((0, semver_1.coerce)(version) || nextHigherVersion, nextHigherVersion);
|
|
58
|
+
}
|
|
59
|
+
function isSonarqubeVersionTwenty_five(version) {
|
|
60
|
+
const nextHigherVersion = '26.0.0';
|
|
61
|
+
return (0, semver_1.lt)((0, semver_1.coerce)(version) || nextHigherVersion, nextHigherVersion);
|
|
62
|
+
}
|
|
14
63
|
const IMPACT_MAPPING = new Map([
|
|
15
64
|
['blocker', 1.0],
|
|
16
65
|
['critical', 0.7],
|
|
@@ -20,62 +69,347 @@ const IMPACT_MAPPING = new Map([
|
|
|
20
69
|
]);
|
|
21
70
|
const CWE_NIST_MAPPING = new CweNistMapping_1.CweNistMapping();
|
|
22
71
|
const OWASP_NIST_MAPPING = new OwaspNistMapping_1.OwaspNistMapping();
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
72
|
+
function parseOwaspInSysTags(issue) {
|
|
73
|
+
return issue.ruleInformation.rule.sysTags
|
|
74
|
+
.filter((s) => s.toLowerCase().startsWith('owasp-'))
|
|
75
|
+
.map((t) => t.substring('owasp-'.length).toUpperCase());
|
|
76
|
+
}
|
|
77
|
+
function parseOwaspTags(issue) {
|
|
78
|
+
let searchSpace = '';
|
|
79
|
+
const rule = issue.ruleInformation.rule;
|
|
80
|
+
if ('htmlDesc' in rule) {
|
|
81
|
+
searchSpace += rule.htmlDesc;
|
|
82
|
+
}
|
|
83
|
+
if (rule.descriptionSections) {
|
|
84
|
+
searchSpace += rule.descriptionSections.map((s) => s.content).join('');
|
|
28
85
|
}
|
|
29
|
-
|
|
30
|
-
|
|
86
|
+
const searchSpaceMatches = [
|
|
87
|
+
...searchSpace.matchAll(/> ?OWASP.*?(Top .*?A\d\d?)/gu)
|
|
88
|
+
].map((m) => m[1]);
|
|
89
|
+
const sysTagMatches = parseOwaspInSysTags(issue);
|
|
90
|
+
const totalMatches = searchSpaceMatches.concat(sysTagMatches);
|
|
91
|
+
if (totalMatches.length) {
|
|
92
|
+
return totalMatches;
|
|
31
93
|
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
function parseCweTags(issue) {
|
|
97
|
+
let searchSpace = '';
|
|
98
|
+
const rule = issue.ruleInformation.rule;
|
|
99
|
+
if ('htmlDesc' in rule) {
|
|
100
|
+
searchSpace += rule.htmlDesc;
|
|
101
|
+
}
|
|
102
|
+
if (rule.descriptionSections) {
|
|
103
|
+
searchSpace += rule.descriptionSections.map((s) => s.content).join('');
|
|
104
|
+
}
|
|
105
|
+
const uniqueCwes = _.uniq(searchSpace.match(/CWE-\d\d\d?\d?\d?\d?\d/gi));
|
|
106
|
+
if (uniqueCwes.length) {
|
|
107
|
+
return uniqueCwes;
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
32
110
|
}
|
|
33
111
|
function parseNistTags(issue) {
|
|
34
112
|
var _a, _b;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
(_b = issue.summary.match(/CWE-\d\d\d?\d?\d?\d?\d/gi)) === null || _b === void 0 ? void 0 : _b.forEach((match) => {
|
|
45
|
-
tags.push(...CWE_NIST_MAPPING.nistFilter(match.split('-')[1]));
|
|
46
|
-
});
|
|
47
|
-
return tags;
|
|
113
|
+
const uniqueNist = _.uniq(((_a = parseCweTags(issue)) !== null && _a !== void 0 ? _a : [])
|
|
114
|
+
.flatMap((t) => CWE_NIST_MAPPING.nistFilter(t.split('-')[1]))
|
|
115
|
+
.concat(((_b = parseOwaspInSysTags(issue)) !== null && _b !== void 0 ? _b : []).flatMap((t) => OWASP_NIST_MAPPING.nistFilterNoDefault(t))));
|
|
116
|
+
if (uniqueNist.length) {
|
|
117
|
+
return uniqueNist;
|
|
118
|
+
}
|
|
119
|
+
return ['SA-11'];
|
|
48
120
|
}
|
|
49
|
-
class
|
|
50
|
-
constructor(
|
|
51
|
-
|
|
52
|
-
|
|
121
|
+
class SonarqubeMapper extends base_converter_1.BaseConverter {
|
|
122
|
+
constructor(data, withRaw = false) {
|
|
123
|
+
super(data);
|
|
124
|
+
this.data = data;
|
|
125
|
+
this.mappings = {
|
|
126
|
+
platform: {
|
|
127
|
+
name: 'Heimdall Tools',
|
|
128
|
+
release: package_json_1.version
|
|
129
|
+
},
|
|
130
|
+
version: package_json_1.version,
|
|
131
|
+
statistics: {},
|
|
132
|
+
profiles: [
|
|
133
|
+
{
|
|
134
|
+
name: 'SonarQube Scan',
|
|
135
|
+
version: {
|
|
136
|
+
transformer: (data) => `SonarQube v${data.sonarqubeVersion}`
|
|
137
|
+
},
|
|
138
|
+
title: {
|
|
139
|
+
transformer: (data) => {
|
|
140
|
+
const branch = data.branchName ? ` branch ${data.branchName}` : '';
|
|
141
|
+
const pullrequest = data.pullRequestID
|
|
142
|
+
? ` pull request ${data.pullRequestID}`
|
|
143
|
+
: '';
|
|
144
|
+
const org = data.organization
|
|
145
|
+
? ` organization ${data.organization}`
|
|
146
|
+
: '';
|
|
147
|
+
return `SonarQube Scan of project ${data.projectKey} on ${data.sonarqubeHost} at ${new Date().toISOString()}${data.branchName || data.pullRequestID || data.organization ? ' using' : ''}${[branch, pullrequest, org].filter((s) => s).join(',')}`;
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
supports: [],
|
|
151
|
+
attributes: [],
|
|
152
|
+
groups: [],
|
|
153
|
+
status: 'loaded',
|
|
154
|
+
controls: [
|
|
155
|
+
{
|
|
156
|
+
path: 'search.issues',
|
|
157
|
+
key: 'id',
|
|
158
|
+
desc: {
|
|
159
|
+
transformer: (issue) => {
|
|
160
|
+
const rule = issue.ruleInformation.rule;
|
|
161
|
+
if ('htmlDesc' in rule) {
|
|
162
|
+
return rule.htmlDesc;
|
|
163
|
+
}
|
|
164
|
+
if (!rule.descriptionSections) {
|
|
165
|
+
return '';
|
|
166
|
+
}
|
|
167
|
+
const def = rule.descriptionSections.find((d) => d.key === 'default');
|
|
168
|
+
if (def) {
|
|
169
|
+
return def.content;
|
|
170
|
+
}
|
|
171
|
+
const introduction = rule.descriptionSections.find((d) => d.key === 'introduction');
|
|
172
|
+
const rootcause = rule.descriptionSections.find((d) => d.key === 'root_cause');
|
|
173
|
+
return [introduction, rootcause]
|
|
174
|
+
.filter((s) => s !== undefined)
|
|
175
|
+
.map((s) => s.content)
|
|
176
|
+
.join('\n');
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
refs: [],
|
|
180
|
+
source_location: {},
|
|
181
|
+
id: { path: 'rule' },
|
|
182
|
+
title: { path: 'ruleInformation.rule.name' },
|
|
183
|
+
impact: {
|
|
184
|
+
path: 'severity',
|
|
185
|
+
transformer: (0, base_converter_1.impactMapping)(IMPACT_MAPPING)
|
|
186
|
+
},
|
|
187
|
+
tags: {
|
|
188
|
+
cci: {
|
|
189
|
+
transformer: (issue) => { var _a; return (0, global_1.getCCIsForNISTTags)((_a = parseNistTags(issue)) !== null && _a !== void 0 ? _a : []); }
|
|
190
|
+
},
|
|
191
|
+
nist: { transformer: parseNistTags },
|
|
192
|
+
cweid: { transformer: parseCweTags },
|
|
193
|
+
owasp: { transformer: parseOwaspTags },
|
|
194
|
+
createdAt: { path: 'ruleInformation.rule.createdAt' },
|
|
195
|
+
debtRemFnType: { path: 'ruleInformation.rule.debtRemFnType' },
|
|
196
|
+
defaultDebtRemFnType: {
|
|
197
|
+
path: 'ruleInformation.rule.defaultDebtRemFnType'
|
|
198
|
+
},
|
|
199
|
+
isExternal: { path: 'ruleInformation.rule.isExternal' },
|
|
200
|
+
isTemplate: { path: 'ruleInformation.rule.isTemplate' },
|
|
201
|
+
langName: { path: 'ruleInformation.rule.langName' },
|
|
202
|
+
remFnBaseEffort: { path: 'ruleInformation.rule.remFnBaseEffort' },
|
|
203
|
+
remFnOverloaded: { path: 'ruleInformation.rule.remFnOverloaded' },
|
|
204
|
+
remFnType: { path: 'ruleInformation.rule.remFnType' },
|
|
205
|
+
repo: { path: 'ruleInformation.rule.repo' },
|
|
206
|
+
scope: { path: 'ruleInformation.rule.scope' },
|
|
207
|
+
ruleSeverity: { path: 'ruleInformation.rule.severity' },
|
|
208
|
+
status: { path: 'ruleInformation.rule.status' },
|
|
209
|
+
transformer: (issue) => {
|
|
210
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
211
|
+
return ({
|
|
212
|
+
...(0, global_1.conditionallyProvideAttribute)('Actives', issue.ruleInformation.actives, issue.ruleInformation.actives.length !== 0),
|
|
213
|
+
...(0, global_1.conditionallyProvideAttribute)('Clean Code Attribute', issue.ruleInformation.rule.cleanCodeAttribute, ((_a = issue.ruleInformation.rule.cleanCodeAttribute) === null || _a === void 0 ? void 0 : _a.length) !== 0),
|
|
214
|
+
...(0, global_1.conditionallyProvideAttribute)('Clean Code Attribute Category', issue.ruleInformation.rule.cleanCodeAttributeCategory, ((_b = issue.ruleInformation.rule.cleanCodeAttributeCategory) === null || _b === void 0 ? void 0 : _b.length) !== 0),
|
|
215
|
+
...(0, global_1.conditionallyProvideAttribute)('Debt Overloaded', 'debtOverloaded' in issue.ruleInformation.rule &&
|
|
216
|
+
issue.ruleInformation.rule.debtOverloaded, 'debtOverloaded' in issue.ruleInformation.rule &&
|
|
217
|
+
issue.ruleInformation.rule.debtOverloaded !== undefined),
|
|
218
|
+
...(0, global_1.conditionallyProvideAttribute)('Debt Rem Fn Coeff', 'debtRemFnCoeff' in issue.ruleInformation.rule &&
|
|
219
|
+
issue.ruleInformation.rule.debtRemFnCoeff, 'debtRemFnCoeff' in issue.ruleInformation.rule &&
|
|
220
|
+
issue.ruleInformation.rule.debtRemFnCoeff !== undefined),
|
|
221
|
+
...(0, global_1.conditionallyProvideAttribute)('Debt Rem Fn Offset', 'debtRemFnOffset' in issue.ruleInformation.rule &&
|
|
222
|
+
issue.ruleInformation.rule.debtRemFnOffset, 'debtRemFnOffset' in issue.ruleInformation.rule),
|
|
223
|
+
...(0, global_1.conditionallyProvideAttribute)('Default Debt Rem Fn Coeff', 'defaultDebtRemFnCoeff' in issue.ruleInformation.rule &&
|
|
224
|
+
issue.ruleInformation.rule.defaultDebtRemFnCoeff, 'defaultDebtRemFnCoeff' in issue.ruleInformation.rule &&
|
|
225
|
+
issue.ruleInformation.rule.defaultDebtRemFnCoeff !==
|
|
226
|
+
undefined),
|
|
227
|
+
...(0, global_1.conditionallyProvideAttribute)('Default Debt Rem Fn Offset', 'defaultDebtRemFnOffset' in issue.ruleInformation.rule &&
|
|
228
|
+
issue.ruleInformation.rule.defaultDebtRemFnOffset, 'defaultDebtRemFnOffset' in issue.ruleInformation.rule),
|
|
229
|
+
...(0, global_1.conditionallyProvideAttribute)('Education Principles', 'educationPrinciples' in issue.ruleInformation.rule &&
|
|
230
|
+
issue.ruleInformation.rule.educationPrinciples, 'educationPrinciples' in issue.ruleInformation.rule &&
|
|
231
|
+
((_c = issue.ruleInformation.rule.educationPrinciples) === null || _c === void 0 ? void 0 : _c.length) !== 0),
|
|
232
|
+
...(0, global_1.conditionallyProvideAttribute)('Effort To Fix Description', 'effortToFixDescription' in issue.ruleInformation.rule &&
|
|
233
|
+
issue.ruleInformation.rule.effortToFixDescription, 'effortToFixDescription' in issue.ruleInformation.rule &&
|
|
234
|
+
issue.ruleInformation.rule.effortToFixDescription !==
|
|
235
|
+
undefined),
|
|
236
|
+
...(0, global_1.conditionallyProvideAttribute)('Impacts', issue.ruleInformation.rule.impacts, ((_d = issue.ruleInformation.rule.impacts) === null || _d === void 0 ? void 0 : _d.length) !== 0),
|
|
237
|
+
...(0, global_1.conditionallyProvideAttribute)('Issue Type Vulnerability', true, issue.type === 'VULNERABILITY'),
|
|
238
|
+
...(0, global_1.conditionallyProvideAttribute)('Issue Type Bug', true, issue.type === 'BUG'),
|
|
239
|
+
...(0, global_1.conditionallyProvideAttribute)('Issue Type Code Smell', true, issue.type === 'CODE_SMELL'),
|
|
240
|
+
...(0, global_1.conditionallyProvideAttribute)('Params', issue.ruleInformation.rule.params, ((_e = issue.ruleInformation.rule.params) === null || _e === void 0 ? void 0 : _e.length) !== 0),
|
|
241
|
+
...(0, global_1.conditionallyProvideAttribute)('Security Standards', issue.ruleInformation.rule.securityStandards, ((_f = issue.ruleInformation.rule.securityStandards) === null || _f === void 0 ? void 0 : _f.length) !== 0),
|
|
242
|
+
...(0, global_1.conditionallyProvideAttribute)('Sys Tags', issue.ruleInformation.rule.sysTags, ((_g = issue.ruleInformation.rule.sysTags) === null || _g === void 0 ? void 0 : _g.length) !== 0),
|
|
243
|
+
...(0, global_1.conditionallyProvideAttribute)('Tags', issue.ruleInformation.rule.tags, ((_h = issue.ruleInformation.rule.tags) === null || _h === void 0 ? void 0 : _h.length) !== 0),
|
|
244
|
+
...(0, global_1.conditionallyProvideAttribute)('Updated At', 'updatedAt' in issue.ruleInformation.rule &&
|
|
245
|
+
issue.ruleInformation.rule.updatedAt, 'updatedAt' in issue.ruleInformation.rule)
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
results: [
|
|
250
|
+
{
|
|
251
|
+
status: inspecjs_1.ExecJSON.ControlResultStatus.Failed,
|
|
252
|
+
code_desc: { path: 'codeSnippet' },
|
|
253
|
+
start_time: { path: 'creationDate' },
|
|
254
|
+
message: {
|
|
255
|
+
transformer: (issue) => {
|
|
256
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
257
|
+
return JSON.stringify({
|
|
258
|
+
Message: issue.message,
|
|
259
|
+
Author: issue.author,
|
|
260
|
+
'Creation Date': issue.creationDate,
|
|
261
|
+
Debt: issue.debt,
|
|
262
|
+
Effort: issue.effort,
|
|
263
|
+
...(0, global_1.conditionallyProvideAttribute)('Issue Status', issue.issueStatus, ((_a = issue.issueStatus) === null || _a === void 0 ? void 0 : _a.length) !== 0),
|
|
264
|
+
...(0, global_1.conditionallyProvideAttribute)('Resolution', issue.resolution, ((_b = issue.resolution) === null || _b === void 0 ? void 0 : _b.length) !== 0),
|
|
265
|
+
Status: issue.status,
|
|
266
|
+
'Update Date': issue.updateDate,
|
|
267
|
+
...(0, global_1.conditionallyProvideAttribute)('Actions', issue.actions, ((_c = issue.actions) === null || _c === void 0 ? void 0 : _c.length) !== 0),
|
|
268
|
+
...(0, global_1.conditionallyProvideAttribute)('Attr', issue.attr, issue.attr !== undefined),
|
|
269
|
+
...(0, global_1.conditionallyProvideAttribute)('Code Variants', 'codeVariants' in issue && issue.codeVariants, 'codeVariants' in issue &&
|
|
270
|
+
((_d = issue.codeVariants) === null || _d === void 0 ? void 0 : _d.length) !== 0),
|
|
271
|
+
...(0, global_1.conditionallyProvideAttribute)('Comments', issue.comments, ((_e = issue.comments) === null || _e === void 0 ? void 0 : _e.length) !== 0),
|
|
272
|
+
...(0, global_1.conditionallyProvideAttribute)('Flows', issue.flows, ((_f = issue.flows) === null || _f === void 0 ? void 0 : _f.length) !== 0),
|
|
273
|
+
...(0, global_1.conditionallyProvideAttribute)('From Hotspot', 'fromHotspot' in issue && issue.fromHotspot, 'fromHotspot' in issue &&
|
|
274
|
+
issue.fromHotspot !== undefined &&
|
|
275
|
+
issue.fromHotspot !== null),
|
|
276
|
+
Hash: issue.hash,
|
|
277
|
+
Key: issue.key,
|
|
278
|
+
...(0, global_1.conditionallyProvideAttribute)('Message Formattings', issue.messageFormattings, ((_g = issue.messageFormattings) === null || _g === void 0 ? void 0 : _g.length) !== 0),
|
|
279
|
+
...(0, global_1.conditionallyProvideAttribute)('Prioritized Rule', 'prioritizedRule' in issue && issue.prioritizedRule, 'prioritizedRule' in issue),
|
|
280
|
+
...(0, global_1.conditionallyProvideAttribute)('Project Name', issue.projectName, ((_h = issue.projectName) === null || _h === void 0 ? void 0 : _h.length) !== 0),
|
|
281
|
+
...(0, global_1.conditionallyProvideAttribute)('Quick Fix Available', 'quickFixAvailable' in issue &&
|
|
282
|
+
issue.quickFixAvailable, 'quickFixAvailable' in issue &&
|
|
283
|
+
issue.quickFixAvailable !== undefined),
|
|
284
|
+
...(0, global_1.conditionallyProvideAttribute)('Rule Description Context Key', 'ruleDescriptionContextKey' in issue &&
|
|
285
|
+
issue.ruleDescriptionContextKey, 'ruleDescriptionContextKey' in issue &&
|
|
286
|
+
((_j = issue.ruleDescriptionContextKey) === null || _j === void 0 ? void 0 : _j.length) !== 0),
|
|
287
|
+
...(0, global_1.conditionallyProvideAttribute)('Tags', issue.tags, ((_k = issue.tags) === null || _k === void 0 ? void 0 : _k.length) !== 0),
|
|
288
|
+
...(0, global_1.conditionallyProvideAttribute)('Transitions', issue.transitions, ((_l = issue.transitions) === null || _l === void 0 ? void 0 : _l.length) !== 0)
|
|
289
|
+
}, null, 2);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
],
|
|
294
|
+
transformer: () => ({
|
|
295
|
+
descriptions: {
|
|
296
|
+
transformer: (issue) => {
|
|
297
|
+
const rule = issue.ruleInformation.rule;
|
|
298
|
+
if (rule.descriptionSections &&
|
|
299
|
+
rule.descriptionSections.length > 0) {
|
|
300
|
+
const def = rule.descriptionSections.find((d) => d.key === 'default');
|
|
301
|
+
const introduction = rule.descriptionSections.find((d) => d.key === 'introduction');
|
|
302
|
+
const rootcause = rule.descriptionSections.find((d) => d.key === 'root_cause');
|
|
303
|
+
const check = rule.descriptionSections.find((d) => d.key === 'assess_the_problem');
|
|
304
|
+
const fix = rule.descriptionSections.find((d) => d.key === 'how_to_fix');
|
|
305
|
+
const remainder = rule.descriptionSections.filter((d) => ![
|
|
306
|
+
'default',
|
|
307
|
+
'introduction',
|
|
308
|
+
'root_cause',
|
|
309
|
+
'assess_the_problem',
|
|
310
|
+
'how_to_fix'
|
|
311
|
+
].includes(d.key));
|
|
312
|
+
const sections = [
|
|
313
|
+
def,
|
|
314
|
+
def ? introduction : undefined,
|
|
315
|
+
def ? rootcause : undefined,
|
|
316
|
+
check,
|
|
317
|
+
fix,
|
|
318
|
+
...remainder
|
|
319
|
+
]
|
|
320
|
+
.filter((s) => s !== undefined)
|
|
321
|
+
.map((s) => ({
|
|
322
|
+
data: s.content,
|
|
323
|
+
label: s.key === 'assess_the_problem'
|
|
324
|
+
? 'check'
|
|
325
|
+
: s.key === 'how_to_fix'
|
|
326
|
+
? 'fix'
|
|
327
|
+
: s.key
|
|
328
|
+
}));
|
|
329
|
+
if (sections) {
|
|
330
|
+
return sections;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
],
|
|
339
|
+
sha256: ''
|
|
340
|
+
}
|
|
341
|
+
],
|
|
342
|
+
passthrough: {
|
|
343
|
+
transformer: (data) => {
|
|
344
|
+
return {
|
|
345
|
+
auxiliary_data: [
|
|
346
|
+
{
|
|
347
|
+
name: 'SonarQube',
|
|
348
|
+
data: {
|
|
349
|
+
..._.omit(data, 'search.issues')
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
],
|
|
353
|
+
...(0, global_1.conditionallyProvideAttribute)('raw', data, this.withRaw)
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
53
357
|
};
|
|
54
|
-
this.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
358
|
+
this.withRaw = withRaw;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
exports.SonarqubeMapper = SonarqubeMapper;
|
|
362
|
+
var AuthenticationMethod;
|
|
363
|
+
(function (AuthenticationMethod) {
|
|
364
|
+
AuthenticationMethod[AuthenticationMethod["TokenAsUsername"] = 0] = "TokenAsUsername";
|
|
365
|
+
AuthenticationMethod[AuthenticationMethod["BearerToken"] = 1] = "BearerToken";
|
|
366
|
+
})(AuthenticationMethod || (AuthenticationMethod = {}));
|
|
367
|
+
class SonarqubeResults {
|
|
368
|
+
constructor(sonarqubeHost, projectKey, userToken, branchName, pullRequestID, organization, withRaw = false) {
|
|
369
|
+
this.sonarqubeHost = sonarqubeHost;
|
|
370
|
+
this.projectKey = projectKey;
|
|
61
371
|
this.userToken = userToken;
|
|
62
372
|
this.branchName = branchName;
|
|
63
373
|
this.pullRequestID = pullRequestID;
|
|
374
|
+
this.organization = organization;
|
|
375
|
+
this.withRaw = withRaw;
|
|
64
376
|
}
|
|
65
|
-
|
|
66
|
-
|
|
377
|
+
logAxiosError(e) {
|
|
378
|
+
if (e.response) {
|
|
379
|
+
logger.debug('response');
|
|
380
|
+
logger.debug(e.response.status);
|
|
381
|
+
logger.debug(e.response.data);
|
|
382
|
+
}
|
|
383
|
+
if (e.request) {
|
|
384
|
+
logger.debug('request');
|
|
385
|
+
logger.debug(e.request);
|
|
386
|
+
}
|
|
387
|
+
if (e.message) {
|
|
388
|
+
logger.debug('message');
|
|
389
|
+
logger.debug('Error', e.message);
|
|
390
|
+
}
|
|
67
391
|
}
|
|
68
|
-
async
|
|
69
|
-
var _a, _b;
|
|
392
|
+
async getSearchResults() {
|
|
70
393
|
let paging = true;
|
|
71
394
|
let page = 1;
|
|
395
|
+
const results = {
|
|
396
|
+
components: [],
|
|
397
|
+
effortTotal: 0,
|
|
398
|
+
facets: [],
|
|
399
|
+
issues: [],
|
|
400
|
+
paging: { pageIndex: 0, pageSize: 0, total: 0 }
|
|
401
|
+
};
|
|
72
402
|
while (paging) {
|
|
73
403
|
await axios_1.default
|
|
74
|
-
.get(`${this.
|
|
75
|
-
|
|
404
|
+
.get(`${this.sonarqubeHost}/api/issues/search`, {
|
|
405
|
+
...(this.authMethod === AuthenticationMethod.TokenAsUsername && {
|
|
406
|
+
auth: { username: this.userToken, password: '' }
|
|
407
|
+
}),
|
|
408
|
+
...(this.authMethod === AuthenticationMethod.BearerToken && {
|
|
409
|
+
headers: { Authorization: `Bearer ${this.userToken}` }
|
|
410
|
+
}),
|
|
76
411
|
params: {
|
|
77
|
-
componentKeys: this.
|
|
78
|
-
types: 'VULNERABILITY',
|
|
412
|
+
componentKeys: this.projectKey,
|
|
79
413
|
statuses: 'OPEN,REOPENED,CONFIRMED,RESOLVED',
|
|
80
414
|
p: page,
|
|
81
415
|
...(this.branchName && { branch: this.branchName }),
|
|
@@ -83,119 +417,145 @@ class SonarQubeResults {
|
|
|
83
417
|
}
|
|
84
418
|
})
|
|
85
419
|
.then(({ data }) => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
paging = ((_a = data.paging) === null || _a === void 0 ? void 0 : _a.total) === 100;
|
|
420
|
+
_.mergeWith(results, data, (objValue, srcValue) => _.isArray(objValue) ? objValue.concat(srcValue) : undefined);
|
|
421
|
+
paging =
|
|
422
|
+
data.paging.pageIndex * data.paging.pageSize <= data.paging.total;
|
|
91
423
|
page += 1;
|
|
424
|
+
})
|
|
425
|
+
.catch((e) => {
|
|
426
|
+
this.logAxiosError(e);
|
|
427
|
+
return Promise.reject(new Error('Failed at getting Sonarqube issue'));
|
|
92
428
|
});
|
|
93
429
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
430
|
+
return results;
|
|
431
|
+
}
|
|
432
|
+
async getCodeSnippets(issues) {
|
|
433
|
+
const getFullFile = async (component) => {
|
|
434
|
+
return axios_1.default
|
|
435
|
+
.get(`${this.sonarqubeHost}/api/sources/raw`, {
|
|
436
|
+
...(this.authMethod === AuthenticationMethod.TokenAsUsername && {
|
|
437
|
+
auth: { username: this.userToken, password: '' }
|
|
438
|
+
}),
|
|
439
|
+
...(this.authMethod === AuthenticationMethod.BearerToken && {
|
|
440
|
+
headers: { Authorization: `Bearer ${this.userToken}` }
|
|
441
|
+
}),
|
|
442
|
+
params: {
|
|
443
|
+
key: component,
|
|
444
|
+
...(this.branchName && { branch: this.branchName }),
|
|
445
|
+
...(this.pullRequestID && { pullRequest: this.pullRequestID })
|
|
446
|
+
},
|
|
447
|
+
responseType: 'text'
|
|
448
|
+
})
|
|
449
|
+
.then(({ data }) => data)
|
|
450
|
+
.catch((e) => {
|
|
451
|
+
this.logAxiosError(e);
|
|
452
|
+
return Promise.reject(new Error(`Failed at getting Sonarqube code snippet for ${component}`));
|
|
453
|
+
});
|
|
454
|
+
};
|
|
455
|
+
const applyLineNumber = (snippet) => snippet
|
|
456
|
+
.split('\n')
|
|
457
|
+
.map((l, i) => `${i + 1} ${l}`)
|
|
458
|
+
.join('\n');
|
|
459
|
+
const getContextualizedSnippet = (fullFiles, component, startLine, endLine, msg) => {
|
|
460
|
+
const linenumberedFile = applyLineNumber(fullFiles[component]);
|
|
461
|
+
const snippet = linenumberedFile
|
|
105
462
|
.split('\n')
|
|
106
|
-
.slice(Math.max(
|
|
463
|
+
.slice(Math.max(startLine - 3, 0), endLine + 3)
|
|
464
|
+
.join('\n')
|
|
465
|
+
.trim();
|
|
466
|
+
const location = `${component}:${startLine}-${endLine}\n`;
|
|
467
|
+
const message = msg ? `${msg}\n` : '';
|
|
468
|
+
return `${location}${message}<pre>\n${snippet}\n</pre>`;
|
|
469
|
+
};
|
|
470
|
+
const components = _.uniq(issues.flatMap((issue) => issue.flows.length
|
|
471
|
+
? issue.flows.flatMap((flow) => flow.locations.map((location) => location.component))
|
|
472
|
+
: [issue.component]));
|
|
473
|
+
const fullFilePromises = await Promise.all(components.map((component) => getFullFile(component)));
|
|
474
|
+
const fullFiles = Object.fromEntries(_.zip(components, fullFilePromises));
|
|
475
|
+
const snippets = issues.map((issue) => issue.flows.length
|
|
476
|
+
? issue.flows
|
|
477
|
+
.flatMap((flow) => flow.locations.map((location) => getContextualizedSnippet(fullFiles, location.component, location.textRange.startLine, location.textRange.endLine, location.msg)))
|
|
107
478
|
.join('\n')
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
479
|
+
: getContextualizedSnippet(fullFiles, issue.component, issue.textRange.startLine, issue.textRange.endLine));
|
|
480
|
+
return snippets;
|
|
481
|
+
}
|
|
482
|
+
async getRules(issues) {
|
|
483
|
+
const getRule = async (rule, organization) => axios_1.default
|
|
484
|
+
.get(`${this.sonarqubeHost}/api/rules/show`, {
|
|
485
|
+
...(this.authMethod === AuthenticationMethod.TokenAsUsername && {
|
|
486
|
+
auth: { username: this.userToken, password: '' }
|
|
487
|
+
}),
|
|
488
|
+
...(this.authMethod === AuthenticationMethod.BearerToken && {
|
|
489
|
+
headers: { Authorization: `Bearer ${this.userToken}` }
|
|
490
|
+
}),
|
|
113
491
|
params: {
|
|
114
|
-
key:
|
|
492
|
+
key: rule,
|
|
493
|
+
...((organization || this.organization) && {
|
|
494
|
+
organization: organization || this.organization
|
|
495
|
+
})
|
|
115
496
|
}
|
|
116
497
|
})
|
|
117
|
-
.then((
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
})
|
|
122
|
-
const
|
|
123
|
-
|
|
498
|
+
.then(({ data }) => data)
|
|
499
|
+
.catch((e) => {
|
|
500
|
+
this.logAxiosError(e);
|
|
501
|
+
return Promise.reject(new Error('Failed at getting Sonarqube rule'));
|
|
502
|
+
});
|
|
503
|
+
const rulesAndOrgs = _.uniqWith(issues.map((issue) => [issue.rule, issue.organization]), _.isEqual);
|
|
504
|
+
const fullRulePromises = await Promise.all(rulesAndOrgs.map((ruleAndOrg) => getRule(...ruleAndOrg)));
|
|
505
|
+
const fullRules = Object.fromEntries(_.zip(rulesAndOrgs.map((ruleAndOrg) => ruleAndOrg.join('\n')), fullRulePromises));
|
|
506
|
+
const rules = issues.map((issue) => fullRules[[issue.rule, issue.organization].join('\n')]);
|
|
507
|
+
return rules;
|
|
124
508
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
summary: `SonarQube Scan of Project ${projectName}${scanDescriptionModifier}`,
|
|
147
|
-
license: null,
|
|
148
|
-
copyright: null,
|
|
149
|
-
copyright_email: null,
|
|
150
|
-
supports: [],
|
|
151
|
-
attributes: [],
|
|
152
|
-
depends: [],
|
|
153
|
-
groups: [],
|
|
154
|
-
status: 'loaded',
|
|
155
|
-
controls: [
|
|
156
|
-
{
|
|
157
|
-
path: 'issues',
|
|
158
|
-
key: 'id',
|
|
159
|
-
desc: { path: 'summary' },
|
|
160
|
-
descriptions: [],
|
|
161
|
-
refs: [],
|
|
162
|
-
source_location: {},
|
|
163
|
-
id: { path: 'rule' },
|
|
164
|
-
title: { path: 'name' },
|
|
165
|
-
impact: {
|
|
166
|
-
path: 'severity',
|
|
167
|
-
transformer: (0, base_converter_1.impactMapping)(IMPACT_MAPPING)
|
|
168
|
-
},
|
|
169
|
-
code: null,
|
|
170
|
-
tags: {
|
|
171
|
-
cci: {
|
|
172
|
-
transformer: (issue) => (0, global_1.getCCIsForNISTTags)(parseNistTags(issue))
|
|
173
|
-
},
|
|
174
|
-
nist: { transformer: parseNistTags }
|
|
175
|
-
},
|
|
176
|
-
results: [
|
|
177
|
-
{
|
|
178
|
-
status: inspecjs_1.ExecJSON.ControlResultStatus.Failed,
|
|
179
|
-
code_desc: { transformer: formatCodeDesc },
|
|
180
|
-
run_time: 0,
|
|
181
|
-
start_time: ''
|
|
182
|
-
}
|
|
183
|
-
]
|
|
184
|
-
}
|
|
185
|
-
],
|
|
186
|
-
sha256: ''
|
|
509
|
+
async generateHdf(sonarqubeVersion) {
|
|
510
|
+
const searchResults = await this.getSearchResults();
|
|
511
|
+
logger.debug(`Got ${searchResults.issues.length} issues`);
|
|
512
|
+
const codeSnippets = await this.getCodeSnippets(searchResults.issues);
|
|
513
|
+
logger.debug(`Got ${codeSnippets.length} code snippets`);
|
|
514
|
+
const rules = await this.getRules(searchResults.issues);
|
|
515
|
+
logger.debug(`Got ${rules.length} rules`);
|
|
516
|
+
const data = {
|
|
517
|
+
sonarqubeVersion,
|
|
518
|
+
sonarqubeHost: this.sonarqubeHost,
|
|
519
|
+
projectKey: this.projectKey,
|
|
520
|
+
branchName: this.branchName,
|
|
521
|
+
pullRequestID: this.pullRequestID,
|
|
522
|
+
organization: this.organization,
|
|
523
|
+
search: {
|
|
524
|
+
...searchResults,
|
|
525
|
+
issues: searchResults.issues.map((issue, index) => ({
|
|
526
|
+
...issue,
|
|
527
|
+
codeSnippet: codeSnippets[index],
|
|
528
|
+
ruleInformation: rules[index]
|
|
529
|
+
}))
|
|
187
530
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.
|
|
196
|
-
this.
|
|
197
|
-
|
|
531
|
+
};
|
|
532
|
+
return new SonarqubeMapper(data, this.withRaw).toHdf();
|
|
533
|
+
}
|
|
534
|
+
async toHdf() {
|
|
535
|
+
const sonarqubeVersion = await axios_1.default
|
|
536
|
+
.get(`${this.sonarqubeHost}/api/server/version`)
|
|
537
|
+
.then(({ data }) => data);
|
|
538
|
+
logger.debug(`Generating HDF for ${this.sonarqubeHost} version: ${sonarqubeVersion}`);
|
|
539
|
+
this.authMethod = isSonarqubeVersionNine(sonarqubeVersion)
|
|
540
|
+
? AuthenticationMethod.TokenAsUsername
|
|
541
|
+
: AuthenticationMethod.BearerToken;
|
|
542
|
+
if (isSonarqubeVersionEight(sonarqubeVersion)) {
|
|
543
|
+
return this.generateHdf(sonarqubeVersion);
|
|
544
|
+
}
|
|
545
|
+
else if (isSonarqubeVersionNine(sonarqubeVersion)) {
|
|
546
|
+
return this.generateHdf(sonarqubeVersion);
|
|
547
|
+
}
|
|
548
|
+
else if (isSonarqubeVersionTen(sonarqubeVersion)) {
|
|
549
|
+
return this.generateHdf(sonarqubeVersion);
|
|
550
|
+
}
|
|
551
|
+
else if (isSonarqubeVersionTwenty_five(sonarqubeVersion)) {
|
|
552
|
+
return this.generateHdf(sonarqubeVersion);
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
logger.debug(`Sonarqube version ${sonarqubeVersion} is not formally supported. Please create an issue at https://github.com/mitre/heimdall2/issues if something is broken.`);
|
|
556
|
+
return this.generateHdf(sonarqubeVersion);
|
|
557
|
+
}
|
|
198
558
|
}
|
|
199
559
|
}
|
|
200
|
-
exports.
|
|
560
|
+
exports.SonarqubeResults = SonarqubeResults;
|
|
201
561
|
//# sourceMappingURL=sonarqube-mapper.js.map
|