mobbdev 1.0.76 → 1.0.80
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.mjs +1133 -669
- package/package.json +13 -10
package/dist/index.mjs
CHANGED
|
@@ -10,32 +10,253 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
10
10
|
import Debug20 from "debug";
|
|
11
11
|
import { hideBin } from "yargs/helpers";
|
|
12
12
|
|
|
13
|
-
// src/
|
|
14
|
-
|
|
15
|
-
addScmToken: "add-scm-token",
|
|
16
|
-
scan: "scan",
|
|
17
|
-
analyze: "analyze",
|
|
18
|
-
review: "review"
|
|
19
|
-
};
|
|
13
|
+
// src/args/commands/convert_to_sarif.ts
|
|
14
|
+
import fs4 from "node:fs";
|
|
20
15
|
|
|
21
|
-
// src/
|
|
22
|
-
import
|
|
23
|
-
import
|
|
16
|
+
// src/commands/convert_to_sarif.ts
|
|
17
|
+
import fs3 from "node:fs";
|
|
18
|
+
import path3 from "node:path";
|
|
24
19
|
|
|
25
|
-
// src/
|
|
26
|
-
import
|
|
20
|
+
// src/commands/fpr_stream_parser.ts
|
|
21
|
+
import fs from "node:fs";
|
|
22
|
+
import sax from "sax";
|
|
23
|
+
var BaseStreamParser = class {
|
|
24
|
+
constructor(parser) {
|
|
25
|
+
__publicField(this, "currentPath", []);
|
|
26
|
+
parser.on("opentag", (tag) => this.onOpenTag(tag));
|
|
27
|
+
parser.on("closetag", () => this.onCloseTag());
|
|
28
|
+
parser.on("text", (text) => this.onText(text));
|
|
29
|
+
}
|
|
30
|
+
getPathString() {
|
|
31
|
+
return this.currentPath.join(" > ");
|
|
32
|
+
}
|
|
33
|
+
onOpenTag(tag) {
|
|
34
|
+
this.currentPath.push(tag.name);
|
|
35
|
+
}
|
|
36
|
+
onCloseTag() {
|
|
37
|
+
this.currentPath.pop();
|
|
38
|
+
}
|
|
39
|
+
onText(_text) {
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var AuditMetadataParser = class extends BaseStreamParser {
|
|
43
|
+
constructor() {
|
|
44
|
+
super(...arguments);
|
|
45
|
+
__publicField(this, "suppressedMap", {});
|
|
46
|
+
}
|
|
47
|
+
onOpenTag(tag) {
|
|
48
|
+
super.onOpenTag(tag);
|
|
49
|
+
switch (this.getPathString()) {
|
|
50
|
+
case "Audit > IssueList > Issue":
|
|
51
|
+
this.suppressedMap[String(tag.attributes["instanceId"] ?? "")] = String(
|
|
52
|
+
tag.attributes["suppressed"] ?? ""
|
|
53
|
+
);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
getAuditMetadata() {
|
|
58
|
+
return this.suppressedMap;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var ReportMetadataParser = class extends BaseStreamParser {
|
|
62
|
+
constructor() {
|
|
63
|
+
super(...arguments);
|
|
64
|
+
__publicField(this, "uuid", "");
|
|
65
|
+
__publicField(this, "createdTSDate", "");
|
|
66
|
+
__publicField(this, "createdTSTime", "");
|
|
67
|
+
__publicField(this, "rules", {});
|
|
68
|
+
__publicField(this, "ruleId", "");
|
|
69
|
+
__publicField(this, "groupName", "");
|
|
70
|
+
}
|
|
71
|
+
onOpenTag(tag) {
|
|
72
|
+
super.onOpenTag(tag);
|
|
73
|
+
switch (this.getPathString()) {
|
|
74
|
+
case "FVDL > EngineData > RuleInfo > Rule":
|
|
75
|
+
this.ruleId = String(tag.attributes["id"] ?? "");
|
|
76
|
+
break;
|
|
77
|
+
case "FVDL > EngineData > RuleInfo > Rule > MetaInfo > Group":
|
|
78
|
+
this.groupName = String(tag.attributes["name"] ?? "");
|
|
79
|
+
break;
|
|
80
|
+
case "FVDL > CreatedTS":
|
|
81
|
+
this.createdTSDate = String(tag.attributes["date"] ?? "");
|
|
82
|
+
this.createdTSTime = String(tag.attributes["time"] ?? "");
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
onText(text) {
|
|
87
|
+
super.onText(text);
|
|
88
|
+
switch (this.getPathString()) {
|
|
89
|
+
case "FVDL > UUID":
|
|
90
|
+
this.uuid = text;
|
|
91
|
+
break;
|
|
92
|
+
case "FVDL > EngineData > RuleInfo > Rule > MetaInfo > Group": {
|
|
93
|
+
const ruleMeta = this.rules[this.ruleId] ?? {};
|
|
94
|
+
ruleMeta[this.groupName] = text;
|
|
95
|
+
this.rules[this.ruleId] = ruleMeta;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
getReportMetadata() {
|
|
101
|
+
return {
|
|
102
|
+
createdTSDate: this.createdTSDate,
|
|
103
|
+
createdTSTime: this.createdTSTime,
|
|
104
|
+
uuid: this.uuid,
|
|
105
|
+
rules: this.rules
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var UnifiedNodePoolParser = class extends BaseStreamParser {
|
|
110
|
+
constructor() {
|
|
111
|
+
super(...arguments);
|
|
112
|
+
__publicField(this, "codePoints", {});
|
|
113
|
+
__publicField(this, "nodeId", "");
|
|
114
|
+
}
|
|
115
|
+
onOpenTag(tag) {
|
|
116
|
+
super.onOpenTag(tag);
|
|
117
|
+
switch (this.getPathString()) {
|
|
118
|
+
case "FVDL > UnifiedNodePool > Node":
|
|
119
|
+
this.nodeId = String(tag.attributes["id"] ?? "");
|
|
120
|
+
break;
|
|
121
|
+
case "FVDL > UnifiedNodePool > Node > SourceLocation":
|
|
122
|
+
this.codePoints[this.nodeId] = {
|
|
123
|
+
path: String(tag.attributes["path"] ?? ""),
|
|
124
|
+
colStart: String(tag.attributes["colStart"] ?? ""),
|
|
125
|
+
colEnd: String(tag.attributes["colEnd"] ?? ""),
|
|
126
|
+
line: String(tag.attributes["line"] ?? ""),
|
|
127
|
+
lineEnd: String(tag.attributes["lineEnd"] ?? "")
|
|
128
|
+
};
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
getNodesPull() {
|
|
133
|
+
return this.codePoints;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
var VulnerabilityParser = class extends BaseStreamParser {
|
|
137
|
+
constructor() {
|
|
138
|
+
super(...arguments);
|
|
139
|
+
__publicField(this, "vulnerabilities", []);
|
|
140
|
+
__publicField(this, "isInVulnerability", false);
|
|
141
|
+
__publicField(this, "codePoints", []);
|
|
142
|
+
__publicField(this, "metadata", {});
|
|
143
|
+
__publicField(this, "metaInfo", {});
|
|
144
|
+
__publicField(this, "groupName", "");
|
|
145
|
+
}
|
|
146
|
+
onOpenTag(tag) {
|
|
147
|
+
super.onOpenTag(tag);
|
|
148
|
+
switch (this.getPathString()) {
|
|
149
|
+
case "FVDL > Vulnerabilities > Vulnerability":
|
|
150
|
+
this.isInVulnerability = true;
|
|
151
|
+
this.metadata = {};
|
|
152
|
+
this.metaInfo = {};
|
|
153
|
+
this.codePoints = [];
|
|
154
|
+
break;
|
|
155
|
+
case "FVDL > Vulnerabilities > Vulnerability > InstanceInfo > MetaInfo > Group":
|
|
156
|
+
this.groupName = String(tag.attributes["name"] ?? "");
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
if (this.isInVulnerability) {
|
|
160
|
+
if (this.getPathString().endsWith(" > Entry > Node > SourceLocation")) {
|
|
161
|
+
this.codePoints.push({
|
|
162
|
+
path: String(tag.attributes["path"] ?? ""),
|
|
163
|
+
colStart: String(tag.attributes["colStart"] ?? ""),
|
|
164
|
+
colEnd: String(tag.attributes["colEnd"] ?? ""),
|
|
165
|
+
line: String(tag.attributes["line"] ?? ""),
|
|
166
|
+
lineEnd: String(tag.attributes["lineEnd"] ?? "")
|
|
167
|
+
});
|
|
168
|
+
} else if (this.getPathString().endsWith(" > Entry > NodeRef")) {
|
|
169
|
+
this.codePoints.push({
|
|
170
|
+
id: String(tag.attributes["id"] ?? "")
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
onText(text) {
|
|
176
|
+
super.onText(text);
|
|
177
|
+
const lastPathSegment = this.currentPath.at(-1);
|
|
178
|
+
if (!this.isInVulnerability || !lastPathSegment) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
switch (this.getPathString()) {
|
|
182
|
+
case "FVDL > Vulnerabilities > Vulnerability > InstanceInfo > InstanceID":
|
|
183
|
+
case "FVDL > Vulnerabilities > Vulnerability > InstanceInfo > InstanceSeverity":
|
|
184
|
+
case "FVDL > Vulnerabilities > Vulnerability > InstanceInfo > Confidence":
|
|
185
|
+
case "FVDL > Vulnerabilities > Vulnerability > ClassInfo > ClassID":
|
|
186
|
+
case "FVDL > Vulnerabilities > Vulnerability > ClassInfo > Type":
|
|
187
|
+
case "FVDL > Vulnerabilities > Vulnerability > ClassInfo > Subtype":
|
|
188
|
+
this.metadata[lastPathSegment] = text;
|
|
189
|
+
break;
|
|
190
|
+
case "FVDL > Vulnerabilities > Vulnerability > InstanceInfo > MetaInfo > Group":
|
|
191
|
+
this.metaInfo[this.groupName] = text;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
onCloseTag() {
|
|
196
|
+
if (this.getPathString() === "FVDL > Vulnerabilities > Vulnerability") {
|
|
197
|
+
this.isInVulnerability = false;
|
|
198
|
+
this.vulnerabilities.push({
|
|
199
|
+
nodes: this.codePoints,
|
|
200
|
+
instanceID: this.metadata["InstanceID"] ?? "",
|
|
201
|
+
instanceSeverity: this.metadata["InstanceSeverity"] ?? "",
|
|
202
|
+
confidence: this.metadata["Confidence"] ?? "",
|
|
203
|
+
classID: this.metadata["ClassID"] ?? "",
|
|
204
|
+
type: this.metadata["Type"] ?? "",
|
|
205
|
+
subtype: this.metadata["Subtype"] ?? "",
|
|
206
|
+
metaInfo: this.metaInfo
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
super.onCloseTag();
|
|
210
|
+
}
|
|
211
|
+
getVulnerabilities() {
|
|
212
|
+
return this.vulnerabilities;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
function initSaxParser(filepath) {
|
|
216
|
+
const parser = sax.createStream(true);
|
|
217
|
+
const awaiter = new Promise((resolve, reject) => {
|
|
218
|
+
parser.on("end", () => resolve(true));
|
|
219
|
+
parser.on("error", (e) => reject(e));
|
|
220
|
+
});
|
|
221
|
+
return {
|
|
222
|
+
parser,
|
|
223
|
+
parse: async () => {
|
|
224
|
+
fs.createReadStream(filepath).pipe(parser);
|
|
225
|
+
await awaiter;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
27
229
|
|
|
28
|
-
// src/
|
|
29
|
-
|
|
30
|
-
|
|
230
|
+
// src/features/analysis/scm/errors.ts
|
|
231
|
+
var InvalidRepoUrlError = class extends Error {
|
|
232
|
+
constructor(m) {
|
|
233
|
+
super(m);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
var InvalidAccessTokenError = class extends Error {
|
|
237
|
+
constructor(m) {
|
|
238
|
+
super(m);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
var InvalidUrlPatternError = class extends Error {
|
|
242
|
+
constructor(m) {
|
|
243
|
+
super(m);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
var RefNotFoundError = class extends Error {
|
|
247
|
+
constructor(m) {
|
|
248
|
+
super(m);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
var RepoNoTokenAccessError = class extends Error {
|
|
252
|
+
constructor(m, scmType) {
|
|
253
|
+
super(m);
|
|
254
|
+
this.scmType = scmType;
|
|
255
|
+
}
|
|
256
|
+
};
|
|
31
257
|
|
|
32
|
-
// src/
|
|
33
|
-
import
|
|
34
|
-
import { fileURLToPath } from "node:url";
|
|
35
|
-
import chalk from "chalk";
|
|
36
|
-
import Debug from "debug";
|
|
37
|
-
import * as dotenv from "dotenv";
|
|
38
|
-
import { z as z8 } from "zod";
|
|
258
|
+
// src/features/analysis/scm/shared/src/types/fix.ts
|
|
259
|
+
import { z as z2 } from "zod";
|
|
39
260
|
|
|
40
261
|
// src/features/analysis/scm/generates/client_generates.ts
|
|
41
262
|
var FixQuestionInputType = /* @__PURE__ */ ((FixQuestionInputType2) => {
|
|
@@ -111,6 +332,7 @@ var IssueLanguage_Enum = /* @__PURE__ */ ((IssueLanguage_Enum2) => {
|
|
|
111
332
|
})(IssueLanguage_Enum || {});
|
|
112
333
|
var IssueType_Enum = /* @__PURE__ */ ((IssueType_Enum2) => {
|
|
113
334
|
IssueType_Enum2["AutoEscapeFalse"] = "AUTO_ESCAPE_FALSE";
|
|
335
|
+
IssueType_Enum2["AvoidBuiltinShadowing"] = "AVOID_BUILTIN_SHADOWING";
|
|
114
336
|
IssueType_Enum2["AvoidIdentityComparisonCachedTypes"] = "AVOID_IDENTITY_COMPARISON_CACHED_TYPES";
|
|
115
337
|
IssueType_Enum2["ClientDomStoredCodeInjection"] = "CLIENT_DOM_STORED_CODE_INJECTION";
|
|
116
338
|
IssueType_Enum2["CmDi"] = "CMDi";
|
|
@@ -140,6 +362,7 @@ var IssueType_Enum = /* @__PURE__ */ ((IssueType_Enum2) => {
|
|
|
140
362
|
IssueType_Enum2["IframeWithoutSandbox"] = "IFRAME_WITHOUT_SANDBOX";
|
|
141
363
|
IssueType_Enum2["ImproperExceptionHandling"] = "IMPROPER_EXCEPTION_HANDLING";
|
|
142
364
|
IssueType_Enum2["ImproperResourceShutdownOrRelease"] = "IMPROPER_RESOURCE_SHUTDOWN_OR_RELEASE";
|
|
365
|
+
IssueType_Enum2["ImproperStringFormatting"] = "IMPROPER_STRING_FORMATTING";
|
|
143
366
|
IssueType_Enum2["IncompleteHostnameRegex"] = "INCOMPLETE_HOSTNAME_REGEX";
|
|
144
367
|
IssueType_Enum2["IncompleteUrlSanitization"] = "INCOMPLETE_URL_SANITIZATION";
|
|
145
368
|
IssueType_Enum2["IncompleteUrlSchemeCheck"] = "INCOMPLETE_URL_SCHEME_CHECK";
|
|
@@ -246,6 +469,7 @@ var Vulnerability_Report_Vendor_Enum = /* @__PURE__ */ ((Vulnerability_Report_Ve
|
|
|
246
469
|
Vulnerability_Report_Vendor_Enum3["Codeql"] = "codeql";
|
|
247
470
|
Vulnerability_Report_Vendor_Enum3["Datadog"] = "datadog";
|
|
248
471
|
Vulnerability_Report_Vendor_Enum3["Fortify"] = "fortify";
|
|
472
|
+
Vulnerability_Report_Vendor_Enum3["FortifyMobbSarif"] = "fortifyMobbSarif";
|
|
249
473
|
Vulnerability_Report_Vendor_Enum3["Opengrep"] = "opengrep";
|
|
250
474
|
Vulnerability_Report_Vendor_Enum3["Semgrep"] = "semgrep";
|
|
251
475
|
Vulnerability_Report_Vendor_Enum3["Snyk"] = "snyk";
|
|
@@ -753,9 +977,6 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
753
977
|
};
|
|
754
978
|
}
|
|
755
979
|
|
|
756
|
-
// src/features/analysis/scm/shared/src/types/fix.ts
|
|
757
|
-
import { z as z2 } from "zod";
|
|
758
|
-
|
|
759
980
|
// src/features/analysis/scm/shared/src/types/shared.ts
|
|
760
981
|
import { z } from "zod";
|
|
761
982
|
var ParsedSeverityZ = z.nativeEnum(Vulnerability_Severity_Enum).nullish().transform((i) => i ?? "low" /* Low */);
|
|
@@ -1177,7 +1398,9 @@ var issueTypeMap = {
|
|
|
1177
1398
|
["RETURN_SHOULD_NOT_BE_INVARIANT" /* ReturnShouldNotBeInvariant */]: "Return Should Not Be Invariant",
|
|
1178
1399
|
["SYSTEM_EXIT_SHOULD_RERAISE" /* SystemExitShouldReraise */]: "SystemExit Should Reraise",
|
|
1179
1400
|
["NO_RETURN_IN_FINALLY" /* NoReturnInFinally */]: "No Return in Finally Block",
|
|
1180
|
-
["AVOID_IDENTITY_COMPARISON_CACHED_TYPES" /* AvoidIdentityComparisonCachedTypes */]: "Avoid Identity Comparison of Cached Types"
|
|
1401
|
+
["AVOID_IDENTITY_COMPARISON_CACHED_TYPES" /* AvoidIdentityComparisonCachedTypes */]: "Avoid Identity Comparison of Cached Types",
|
|
1402
|
+
["AVOID_BUILTIN_SHADOWING" /* AvoidBuiltinShadowing */]: "Avoid Builtin Shadowing",
|
|
1403
|
+
["IMPROPER_STRING_FORMATTING" /* ImproperStringFormatting */]: "Improper String Formatting"
|
|
1181
1404
|
};
|
|
1182
1405
|
var issueTypeZ = z5.nativeEnum(IssueType_Enum);
|
|
1183
1406
|
var getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -1630,290 +1853,49 @@ var ScmType = /* @__PURE__ */ ((ScmType2) => {
|
|
|
1630
1853
|
ScmType2["Bitbucket"] = "Bitbucket";
|
|
1631
1854
|
return ScmType2;
|
|
1632
1855
|
})(ScmType || {});
|
|
1856
|
+
var ConvertToSarifInputFileFormat = /* @__PURE__ */ ((ConvertToSarifInputFileFormat2) => {
|
|
1857
|
+
ConvertToSarifInputFileFormat2["FortifyFPR"] = "FortifyFPR";
|
|
1858
|
+
return ConvertToSarifInputFileFormat2;
|
|
1859
|
+
})(ConvertToSarifInputFileFormat || {});
|
|
1633
1860
|
|
|
1634
|
-
// src/constants.ts
|
|
1635
|
-
var
|
|
1636
|
-
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
1637
|
-
dotenv.config({ path: path.join(__dirname, "../.env") });
|
|
1638
|
-
var scmFriendlyText = {
|
|
1639
|
-
["Ado" /* Ado */]: "Azure DevOps",
|
|
1640
|
-
["Bitbucket" /* Bitbucket */]: "Bitbucket",
|
|
1641
|
-
["GitHub" /* GitHub */]: "GitGub",
|
|
1642
|
-
["GitLab" /* GitLab */]: "GitLab"
|
|
1643
|
-
};
|
|
1644
|
-
var SCANNERS = {
|
|
1645
|
-
Checkmarx: "checkmarx",
|
|
1646
|
-
Codeql: "codeql",
|
|
1647
|
-
Fortify: "fortify",
|
|
1648
|
-
Snyk: "snyk",
|
|
1649
|
-
Sonarqube: "sonarqube",
|
|
1650
|
-
Semgrep: "semgrep",
|
|
1651
|
-
Datadog: "datadog"
|
|
1652
|
-
};
|
|
1653
|
-
var scannerToVulnerability_Report_Vendor_Enum = {
|
|
1654
|
-
[SCANNERS.Checkmarx]: "checkmarx" /* Checkmarx */,
|
|
1655
|
-
[SCANNERS.Snyk]: "snyk" /* Snyk */,
|
|
1656
|
-
[SCANNERS.Sonarqube]: "sonarqube" /* Sonarqube */,
|
|
1657
|
-
[SCANNERS.Codeql]: "codeql" /* Codeql */,
|
|
1658
|
-
[SCANNERS.Fortify]: "fortify" /* Fortify */,
|
|
1659
|
-
[SCANNERS.Semgrep]: "semgrep" /* Semgrep */,
|
|
1660
|
-
[SCANNERS.Datadog]: "datadog" /* Datadog */
|
|
1661
|
-
};
|
|
1662
|
-
var SupportedScannersZ = z8.enum([SCANNERS.Checkmarx, SCANNERS.Snyk]);
|
|
1663
|
-
var envVariablesSchema = z8.object({
|
|
1664
|
-
WEB_APP_URL: z8.string(),
|
|
1665
|
-
API_URL: z8.string(),
|
|
1666
|
-
HASURA_ACCESS_KEY: z8.string(),
|
|
1667
|
-
LOCAL_GRAPHQL_ENDPOINT: z8.string(),
|
|
1668
|
-
HTTP_PROXY: z8.string().optional().default(""),
|
|
1669
|
-
HTTPS_PROXY: z8.string().optional().default("")
|
|
1670
|
-
}).required();
|
|
1671
|
-
var envVariables = envVariablesSchema.parse(process.env);
|
|
1672
|
-
debug("config %o", envVariables);
|
|
1673
|
-
var mobbAscii = `
|
|
1674
|
-
..
|
|
1675
|
-
..........
|
|
1676
|
-
.................
|
|
1677
|
-
...........................
|
|
1678
|
-
..............................
|
|
1679
|
-
................................
|
|
1680
|
-
..................................
|
|
1681
|
-
....................................
|
|
1682
|
-
.....................................
|
|
1683
|
-
.............................................
|
|
1684
|
-
.................................................
|
|
1685
|
-
............................... .................
|
|
1686
|
-
.................................. ............
|
|
1687
|
-
.................. ............. ..........
|
|
1688
|
-
......... ........ ......... ......
|
|
1689
|
-
............... ....
|
|
1690
|
-
.... ..
|
|
1691
|
-
|
|
1692
|
-
. ...
|
|
1693
|
-
..............
|
|
1694
|
-
......................
|
|
1695
|
-
...........................
|
|
1696
|
-
................................
|
|
1697
|
-
......................................
|
|
1698
|
-
...............................
|
|
1699
|
-
.................
|
|
1700
|
-
`;
|
|
1701
|
-
var PROJECT_DEFAULT_NAME = "My first project";
|
|
1702
|
-
var WEB_APP_URL = envVariables.WEB_APP_URL;
|
|
1703
|
-
var API_URL = envVariables.API_URL;
|
|
1704
|
-
var HASURA_ACCESS_KEY = envVariables.HASURA_ACCESS_KEY;
|
|
1705
|
-
var LOCAL_GRAPHQL_ENDPOINT = envVariables.LOCAL_GRAPHQL_ENDPOINT;
|
|
1706
|
-
var HTTPS_PROXY = envVariables.HTTPS_PROXY;
|
|
1707
|
-
var HTTP_PROXY = envVariables.HTTP_PROXY;
|
|
1708
|
-
var errorMessages = {
|
|
1709
|
-
missingCxProjectName: `project name ${chalk.bold(
|
|
1710
|
-
"(--cx-project-name)"
|
|
1711
|
-
)} is needed if you're using checkmarx`,
|
|
1712
|
-
missingUrl: `url ${chalk.bold(
|
|
1713
|
-
"(--url)"
|
|
1714
|
-
)} is needed if you're adding an SCM token`,
|
|
1715
|
-
invalidScmType: `SCM type ${chalk.bold(
|
|
1716
|
-
"(--scm-type)"
|
|
1717
|
-
)} is invalid, please use one of: ${Object.values(ScmType).join(", ")}`,
|
|
1718
|
-
missingToken: `SCM token ${chalk.bold(
|
|
1719
|
-
"(--token)"
|
|
1720
|
-
)} is needed if you're adding an SCM token`
|
|
1721
|
-
};
|
|
1722
|
-
var progressMassages = {
|
|
1723
|
-
processingVulnerabilityReportSuccess: "\u2699\uFE0F Vulnerability report proccessed successfully",
|
|
1724
|
-
processingVulnerabilityReport: "\u2699\uFE0F Proccessing vulnerability report",
|
|
1725
|
-
processingVulnerabilityReportFailed: "\u2699\uFE0F Error Proccessing vulnerability report"
|
|
1726
|
-
};
|
|
1727
|
-
var VUL_REPORT_DIGEST_TIMEOUT_MS = 1e3 * 60 * 30;
|
|
1861
|
+
// src/features/analysis/scm/ado/constants.ts
|
|
1862
|
+
var DEFUALT_ADO_ORIGIN = scmCloudUrl.Ado;
|
|
1728
1863
|
|
|
1729
|
-
// src/features/analysis/
|
|
1730
|
-
import
|
|
1731
|
-
import
|
|
1732
|
-
import
|
|
1733
|
-
import {
|
|
1734
|
-
import { pipeline } from "node:stream/promises";
|
|
1864
|
+
// src/features/analysis/scm/ado/utils.ts
|
|
1865
|
+
import querystring from "node:querystring";
|
|
1866
|
+
import * as api from "azure-devops-node-api";
|
|
1867
|
+
import Debug from "debug";
|
|
1868
|
+
import { z as z17 } from "zod";
|
|
1735
1869
|
|
|
1736
|
-
// src/
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
getTopLevelDirName: () => getTopLevelDirName,
|
|
1743
|
-
keypress: () => keypress,
|
|
1744
|
-
packageJson: () => packageJson,
|
|
1745
|
-
sleep: () => sleep
|
|
1870
|
+
// src/features/analysis/scm/env.ts
|
|
1871
|
+
import { z as z8 } from "zod";
|
|
1872
|
+
var EnvVariablesZod = z8.object({
|
|
1873
|
+
GITLAB_API_TOKEN: z8.string().optional(),
|
|
1874
|
+
GITHUB_API_TOKEN: z8.string().optional(),
|
|
1875
|
+
GIT_PROXY_HOST: z8.string().optional().default("http://tinyproxy:8888")
|
|
1746
1876
|
});
|
|
1877
|
+
var { GITLAB_API_TOKEN, GITHUB_API_TOKEN, GIT_PROXY_HOST } = EnvVariablesZod.parse(process.env);
|
|
1747
1878
|
|
|
1748
|
-
// src/
|
|
1749
|
-
import
|
|
1750
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1751
|
-
function getDirName() {
|
|
1752
|
-
return path2.dirname(fileURLToPath2(import.meta.url));
|
|
1753
|
-
}
|
|
1754
|
-
function getTopLevelDirName(fullPath) {
|
|
1755
|
-
return path2.parse(fullPath).name;
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
// src/utils/keypress.ts
|
|
1759
|
-
import readline from "node:readline";
|
|
1760
|
-
async function keypress() {
|
|
1761
|
-
const rl = readline.createInterface({
|
|
1762
|
-
input: process.stdin,
|
|
1763
|
-
output: process.stdout
|
|
1764
|
-
});
|
|
1765
|
-
return new Promise((resolve) => {
|
|
1766
|
-
rl.question("", (answer) => {
|
|
1767
|
-
rl.close();
|
|
1768
|
-
process.stderr.moveCursor(0, -1);
|
|
1769
|
-
process.stderr.clearLine(1);
|
|
1770
|
-
resolve(answer);
|
|
1771
|
-
});
|
|
1772
|
-
});
|
|
1773
|
-
}
|
|
1879
|
+
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
1880
|
+
import { z as z9 } from "zod";
|
|
1774
1881
|
|
|
1775
|
-
// src/
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
if (opts?.text) console.error(opts.text);
|
|
1781
|
-
}
|
|
1782
|
-
var mockSpinner = {
|
|
1783
|
-
success: (opts) => {
|
|
1784
|
-
printToStdError(opts);
|
|
1785
|
-
return mockSpinner;
|
|
1882
|
+
// src/features/analysis/scm/shared/src/fixDetailsData.ts
|
|
1883
|
+
var fixDetailsData = {
|
|
1884
|
+
["PT" /* Pt */]: {
|
|
1885
|
+
issueDescription: "Path Traversal AKA Directory Traversal occurs when a path coming from user input is not properly sanitized, allowing an attacker to navigate through directories beyond the intended scope. Attackers can exploit this to access sensitive files or execute arbitrary code.",
|
|
1886
|
+
fixInstructions: "Sanitize user-supplied paths, ensuring that they are restricted to a predefined directory structure."
|
|
1786
1887
|
},
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1888
|
+
["ZIP_SLIP" /* ZipSlip */]: {
|
|
1889
|
+
issueDescription: "Zip Slip is a form of directory traversal that can be exploited by extracting files from an archive. Attackers can manipulate archive files to overwrite sensitive files or execute arbitrary code.",
|
|
1890
|
+
fixInstructions: "Ensure that extracted files are relative and within the intended directory structure."
|
|
1790
1891
|
},
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1892
|
+
["XSS" /* Xss */]: {
|
|
1893
|
+
issueDescription: "Cross-Site Scripting (XSS) allows attackers to inject malicious scripts into web pages viewed by other users. This can lead to theft of session cookies, redirection to malicious websites, or defacement of the webpage.",
|
|
1894
|
+
fixInstructions: "Implement input validation and output encoding. This includes sanitizing user input and escaping special characters to prevent execution of injected scripts."
|
|
1794
1895
|
},
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
},
|
|
1799
|
-
start: (opts) => {
|
|
1800
|
-
printToStdError(opts);
|
|
1801
|
-
return mockSpinner;
|
|
1802
|
-
},
|
|
1803
|
-
update: (opts) => {
|
|
1804
|
-
printToStdError(opts);
|
|
1805
|
-
return mockSpinner;
|
|
1806
|
-
},
|
|
1807
|
-
reset: () => mockSpinner,
|
|
1808
|
-
clear: () => mockSpinner,
|
|
1809
|
-
spin: () => mockSpinner
|
|
1810
|
-
};
|
|
1811
|
-
function Spinner({ ci = false } = {}) {
|
|
1812
|
-
return {
|
|
1813
|
-
createSpinner: (text, options) => ci ? mockSpinner : _createSpinner(text, options)
|
|
1814
|
-
};
|
|
1815
|
-
}
|
|
1816
|
-
|
|
1817
|
-
// src/utils/check_node_version.ts
|
|
1818
|
-
import fs from "node:fs";
|
|
1819
|
-
import path3 from "node:path";
|
|
1820
|
-
import semver from "semver";
|
|
1821
|
-
var packageJson = JSON.parse(
|
|
1822
|
-
fs.readFileSync(path3.join(getDirName(), "../package.json"), "utf8")
|
|
1823
|
-
);
|
|
1824
|
-
if (!semver.satisfies(process.version, packageJson.engines.node)) {
|
|
1825
|
-
console.error(
|
|
1826
|
-
`
|
|
1827
|
-
\u26A0\uFE0F ${packageJson.name} requires node version ${packageJson.engines.node}, but running ${process.version}.`
|
|
1828
|
-
);
|
|
1829
|
-
process.exit(1);
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
// src/utils/index.ts
|
|
1833
|
-
var sleep = (ms = 2e3) => new Promise((r) => setTimeout(r, ms));
|
|
1834
|
-
var CliError = class extends Error {
|
|
1835
|
-
};
|
|
1836
|
-
|
|
1837
|
-
// src/features/analysis/index.ts
|
|
1838
|
-
import chalk4 from "chalk";
|
|
1839
|
-
import Configstore from "configstore";
|
|
1840
|
-
import Debug18 from "debug";
|
|
1841
|
-
import extract from "extract-zip";
|
|
1842
|
-
import { createSpinner as createSpinner4 } from "nanospinner";
|
|
1843
|
-
import fetch4 from "node-fetch";
|
|
1844
|
-
import open2 from "open";
|
|
1845
|
-
import tmp from "tmp";
|
|
1846
|
-
import { z as z29 } from "zod";
|
|
1847
|
-
|
|
1848
|
-
// src/features/analysis/add_fix_comments_for_pr/add_fix_comments_for_pr.ts
|
|
1849
|
-
import Debug8 from "debug";
|
|
1850
|
-
|
|
1851
|
-
// src/features/analysis/scm/errors.ts
|
|
1852
|
-
var InvalidRepoUrlError = class extends Error {
|
|
1853
|
-
constructor(m) {
|
|
1854
|
-
super(m);
|
|
1855
|
-
}
|
|
1856
|
-
};
|
|
1857
|
-
var InvalidAccessTokenError = class extends Error {
|
|
1858
|
-
constructor(m) {
|
|
1859
|
-
super(m);
|
|
1860
|
-
}
|
|
1861
|
-
};
|
|
1862
|
-
var InvalidUrlPatternError = class extends Error {
|
|
1863
|
-
constructor(m) {
|
|
1864
|
-
super(m);
|
|
1865
|
-
}
|
|
1866
|
-
};
|
|
1867
|
-
var RefNotFoundError = class extends Error {
|
|
1868
|
-
constructor(m) {
|
|
1869
|
-
super(m);
|
|
1870
|
-
}
|
|
1871
|
-
};
|
|
1872
|
-
var RepoNoTokenAccessError = class extends Error {
|
|
1873
|
-
constructor(m, scmType) {
|
|
1874
|
-
super(m);
|
|
1875
|
-
this.scmType = scmType;
|
|
1876
|
-
}
|
|
1877
|
-
};
|
|
1878
|
-
|
|
1879
|
-
// src/features/analysis/scm/ado/constants.ts
|
|
1880
|
-
var DEFUALT_ADO_ORIGIN = scmCloudUrl.Ado;
|
|
1881
|
-
|
|
1882
|
-
// src/features/analysis/scm/ado/utils.ts
|
|
1883
|
-
import querystring from "node:querystring";
|
|
1884
|
-
import * as api from "azure-devops-node-api";
|
|
1885
|
-
import Debug2 from "debug";
|
|
1886
|
-
import { z as z18 } from "zod";
|
|
1887
|
-
|
|
1888
|
-
// src/features/analysis/scm/env.ts
|
|
1889
|
-
import { z as z9 } from "zod";
|
|
1890
|
-
var EnvVariablesZod = z9.object({
|
|
1891
|
-
GITLAB_API_TOKEN: z9.string().optional(),
|
|
1892
|
-
GITHUB_API_TOKEN: z9.string().optional(),
|
|
1893
|
-
GIT_PROXY_HOST: z9.string()
|
|
1894
|
-
});
|
|
1895
|
-
var { GITLAB_API_TOKEN, GITHUB_API_TOKEN, GIT_PROXY_HOST } = EnvVariablesZod.parse(process.env);
|
|
1896
|
-
|
|
1897
|
-
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
1898
|
-
import { z as z10 } from "zod";
|
|
1899
|
-
|
|
1900
|
-
// src/features/analysis/scm/shared/src/fixDetailsData.ts
|
|
1901
|
-
var fixDetailsData = {
|
|
1902
|
-
["PT" /* Pt */]: {
|
|
1903
|
-
issueDescription: "Path Traversal AKA Directory Traversal occurs when a path coming from user input is not properly sanitized, allowing an attacker to navigate through directories beyond the intended scope. Attackers can exploit this to access sensitive files or execute arbitrary code.",
|
|
1904
|
-
fixInstructions: "Sanitize user-supplied paths, ensuring that they are restricted to a predefined directory structure."
|
|
1905
|
-
},
|
|
1906
|
-
["ZIP_SLIP" /* ZipSlip */]: {
|
|
1907
|
-
issueDescription: "Zip Slip is a form of directory traversal that can be exploited by extracting files from an archive. Attackers can manipulate archive files to overwrite sensitive files or execute arbitrary code.",
|
|
1908
|
-
fixInstructions: "Ensure that extracted files are relative and within the intended directory structure."
|
|
1909
|
-
},
|
|
1910
|
-
["XSS" /* Xss */]: {
|
|
1911
|
-
issueDescription: "Cross-Site Scripting (XSS) allows attackers to inject malicious scripts into web pages viewed by other users. This can lead to theft of session cookies, redirection to malicious websites, or defacement of the webpage.",
|
|
1912
|
-
fixInstructions: "Implement input validation and output encoding. This includes sanitizing user input and escaping special characters to prevent execution of injected scripts."
|
|
1913
|
-
},
|
|
1914
|
-
["XXE" /* Xxe */]: {
|
|
1915
|
-
issueDescription: "XML External Entity (XXE) allows attackers to exploit vulnerable XML processors by including external entities, leading to disclosure of confidential data, denial of service, or server-side request forgery.",
|
|
1916
|
-
fixInstructions: "Disable external entity processing in XML parsers or update to versions that mitigate XXE vulnerabilities. Input validation should be implemented to ensure that XML input does not contain external entity references."
|
|
1896
|
+
["XXE" /* Xxe */]: {
|
|
1897
|
+
issueDescription: "XML External Entity (XXE) allows attackers to exploit vulnerable XML processors by including external entities, leading to disclosure of confidential data, denial of service, or server-side request forgery.",
|
|
1898
|
+
fixInstructions: "Disable external entity processing in XML parsers or update to versions that mitigate XXE vulnerabilities. Input validation should be implemented to ensure that XML input does not contain external entity references."
|
|
1917
1899
|
},
|
|
1918
1900
|
["CMDi" /* CmDi */]: {
|
|
1919
1901
|
issueDescription: "Command Injection (CMDI) allows attackers inject malicious commands into vulnerable applications, that can result in execution of arbitrary commands on the underlying operating system.",
|
|
@@ -2147,7 +2129,9 @@ var fixDetailsData = {
|
|
|
2147
2129
|
["RETURN_SHOULD_NOT_BE_INVARIANT" /* ReturnShouldNotBeInvariant */]: void 0,
|
|
2148
2130
|
["SYSTEM_EXIT_SHOULD_RERAISE" /* SystemExitShouldReraise */]: void 0,
|
|
2149
2131
|
["NO_RETURN_IN_FINALLY" /* NoReturnInFinally */]: void 0,
|
|
2150
|
-
["AVOID_IDENTITY_COMPARISON_CACHED_TYPES" /* AvoidIdentityComparisonCachedTypes */]: void 0
|
|
2132
|
+
["AVOID_IDENTITY_COMPARISON_CACHED_TYPES" /* AvoidIdentityComparisonCachedTypes */]: void 0,
|
|
2133
|
+
["AVOID_BUILTIN_SHADOWING" /* AvoidBuiltinShadowing */]: void 0,
|
|
2134
|
+
["IMPROPER_STRING_FORMATTING" /* ImproperStringFormatting */]: void 0
|
|
2151
2135
|
};
|
|
2152
2136
|
|
|
2153
2137
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -2178,7 +2162,7 @@ var getCommitDescription = ({
|
|
|
2178
2162
|
)}**.
|
|
2179
2163
|
|
|
2180
2164
|
`;
|
|
2181
|
-
const parseIssueTypeRes =
|
|
2165
|
+
const parseIssueTypeRes = z9.nativeEnum(IssueType_Enum).safeParse(issueType);
|
|
2182
2166
|
if (issueType && parseIssueTypeRes.success) {
|
|
2183
2167
|
if (irrelevantIssueWithTags?.[0]?.tag) {
|
|
2184
2168
|
description += `
|
|
@@ -2221,7 +2205,7 @@ var getCommitIssueDescription = ({
|
|
|
2221
2205
|
const issueTypeString = getIssueTypeFriendlyString(issueType);
|
|
2222
2206
|
let description = `The following issues reported by ${capitalizeFirstLetter(vendor)} on this PR were found to be irrelevant to your project:
|
|
2223
2207
|
`;
|
|
2224
|
-
const parseIssueTypeRes =
|
|
2208
|
+
const parseIssueTypeRes = z9.nativeEnum(IssueType_Enum).safeParse(issueType);
|
|
2225
2209
|
if (issueType && parseIssueTypeRes.success) {
|
|
2226
2210
|
if (irrelevantIssueWithTags?.[0]?.tag) {
|
|
2227
2211
|
description = `
|
|
@@ -2246,10 +2230,10 @@ ${staticData.issueDescription}
|
|
|
2246
2230
|
};
|
|
2247
2231
|
|
|
2248
2232
|
// src/features/analysis/scm/shared/src/guidances.ts
|
|
2249
|
-
import { z as
|
|
2233
|
+
import { z as z12 } from "zod";
|
|
2250
2234
|
|
|
2251
2235
|
// src/features/analysis/scm/shared/src/storedFixData/index.ts
|
|
2252
|
-
import { z as
|
|
2236
|
+
import { z as z10 } from "zod";
|
|
2253
2237
|
|
|
2254
2238
|
// src/features/analysis/scm/shared/src/storedFixData/passwordInComment.ts
|
|
2255
2239
|
var passwordInComment = {
|
|
@@ -2425,8 +2409,8 @@ var vulnerabilities8 = {
|
|
|
2425
2409
|
var xml_default = vulnerabilities8;
|
|
2426
2410
|
|
|
2427
2411
|
// src/features/analysis/scm/shared/src/storedFixData/index.ts
|
|
2428
|
-
var StoredFixDataItemZ =
|
|
2429
|
-
guidance:
|
|
2412
|
+
var StoredFixDataItemZ = z10.object({
|
|
2413
|
+
guidance: z10.function().returns(z10.string())
|
|
2430
2414
|
});
|
|
2431
2415
|
var languages = {
|
|
2432
2416
|
["Java" /* Java */]: java_default,
|
|
@@ -2440,7 +2424,7 @@ var languages = {
|
|
|
2440
2424
|
};
|
|
2441
2425
|
|
|
2442
2426
|
// src/features/analysis/scm/shared/src/storedQuestionData/index.ts
|
|
2443
|
-
import { z as
|
|
2427
|
+
import { z as z11 } from "zod";
|
|
2444
2428
|
|
|
2445
2429
|
// src/features/analysis/scm/shared/src/storedQuestionData/csharp/httpOnlyCookie.ts
|
|
2446
2430
|
var httpOnlyCookie = {
|
|
@@ -3639,10 +3623,10 @@ var vulnerabilities14 = {
|
|
|
3639
3623
|
var xml_default2 = vulnerabilities14;
|
|
3640
3624
|
|
|
3641
3625
|
// src/features/analysis/scm/shared/src/storedQuestionData/index.ts
|
|
3642
|
-
var StoredQuestionDataItemZ =
|
|
3643
|
-
content:
|
|
3644
|
-
description:
|
|
3645
|
-
guidance:
|
|
3626
|
+
var StoredQuestionDataItemZ = z11.object({
|
|
3627
|
+
content: z11.function().args(z11.any()).returns(z11.string()),
|
|
3628
|
+
description: z11.function().args(z11.any()).returns(z11.string()),
|
|
3629
|
+
guidance: z11.function().args(z11.any()).returns(z11.string())
|
|
3646
3630
|
});
|
|
3647
3631
|
var languages2 = {
|
|
3648
3632
|
["Java" /* Java */]: java_default2,
|
|
@@ -3737,9 +3721,9 @@ function getFixGuidances({
|
|
|
3737
3721
|
const fixGuidance = storeFixResult.success ? [storeFixResult.data.guidance({ questions, ...extraContext })] : [];
|
|
3738
3722
|
return libGuidances.concat(fixGuidance).filter((guidance) => !!guidance);
|
|
3739
3723
|
}
|
|
3740
|
-
var IssueTypeAndLanguageZ =
|
|
3741
|
-
issueType:
|
|
3742
|
-
issueLanguage:
|
|
3724
|
+
var IssueTypeAndLanguageZ = z12.object({
|
|
3725
|
+
issueType: z12.nativeEnum(IssueType_Enum),
|
|
3726
|
+
issueLanguage: z12.nativeEnum(IssueLanguage_Enum)
|
|
3743
3727
|
});
|
|
3744
3728
|
function getGuidances(args) {
|
|
3745
3729
|
const safeIssueTypeAndLanguage = IssueTypeAndLanguageZ.safeParse({
|
|
@@ -3777,7 +3761,7 @@ function getGuidances(args) {
|
|
|
3777
3761
|
}
|
|
3778
3762
|
|
|
3779
3763
|
// src/features/analysis/scm/shared/src/urlParser/urlParser.ts
|
|
3780
|
-
import { z as
|
|
3764
|
+
import { z as z13 } from "zod";
|
|
3781
3765
|
var ADO_PREFIX_PATH = "tfs";
|
|
3782
3766
|
var NAME_REGEX = /[a-z0-9\-_.+]+/i;
|
|
3783
3767
|
function detectAdoUrl(args) {
|
|
@@ -3794,7 +3778,7 @@ function detectAdoUrl(args) {
|
|
|
3794
3778
|
scmType: "Ado" /* Ado */,
|
|
3795
3779
|
organization,
|
|
3796
3780
|
// project has single repo - repoName === projectName
|
|
3797
|
-
projectName:
|
|
3781
|
+
projectName: z13.string().parse(projectName),
|
|
3798
3782
|
repoName: projectName,
|
|
3799
3783
|
prefixPath
|
|
3800
3784
|
};
|
|
@@ -3805,7 +3789,7 @@ function detectAdoUrl(args) {
|
|
|
3805
3789
|
return {
|
|
3806
3790
|
scmType: "Ado" /* Ado */,
|
|
3807
3791
|
organization,
|
|
3808
|
-
projectName:
|
|
3792
|
+
projectName: z13.string().parse(projectName),
|
|
3809
3793
|
repoName,
|
|
3810
3794
|
prefixPath
|
|
3811
3795
|
};
|
|
@@ -3819,7 +3803,7 @@ function detectAdoUrl(args) {
|
|
|
3819
3803
|
scmType: "Ado" /* Ado */,
|
|
3820
3804
|
organization,
|
|
3821
3805
|
// project has only one repo - repoName === projectName
|
|
3822
|
-
projectName:
|
|
3806
|
+
projectName: z13.string().parse(repoName),
|
|
3823
3807
|
repoName,
|
|
3824
3808
|
prefixPath
|
|
3825
3809
|
};
|
|
@@ -3829,7 +3813,7 @@ function detectAdoUrl(args) {
|
|
|
3829
3813
|
return {
|
|
3830
3814
|
scmType: "Ado" /* Ado */,
|
|
3831
3815
|
organization,
|
|
3832
|
-
projectName:
|
|
3816
|
+
projectName: z13.string().parse(projectName),
|
|
3833
3817
|
repoName,
|
|
3834
3818
|
prefixPath
|
|
3835
3819
|
};
|
|
@@ -3956,10 +3940,10 @@ function getIssueUrl({
|
|
|
3956
3940
|
}
|
|
3957
3941
|
|
|
3958
3942
|
// src/features/analysis/scm/utils/index.ts
|
|
3959
|
-
import { z as
|
|
3943
|
+
import { z as z15 } from "zod";
|
|
3960
3944
|
|
|
3961
3945
|
// src/features/analysis/scm/types.ts
|
|
3962
|
-
import { z as
|
|
3946
|
+
import { z as z14 } from "zod";
|
|
3963
3947
|
var ReferenceType = /* @__PURE__ */ ((ReferenceType2) => {
|
|
3964
3948
|
ReferenceType2["BRANCH"] = "BRANCH";
|
|
3965
3949
|
ReferenceType2["COMMIT"] = "COMMIT";
|
|
@@ -3991,10 +3975,10 @@ var scmTypeToScmLibScmType = {
|
|
|
3991
3975
|
["Ado" /* Ado */]: "ADO" /* ADO */,
|
|
3992
3976
|
["Bitbucket" /* Bitbucket */]: "BITBUCKET" /* BITBUCKET */
|
|
3993
3977
|
};
|
|
3994
|
-
var GetRefererenceResultZ =
|
|
3995
|
-
date:
|
|
3996
|
-
sha:
|
|
3997
|
-
type:
|
|
3978
|
+
var GetRefererenceResultZ = z14.object({
|
|
3979
|
+
date: z14.date().optional(),
|
|
3980
|
+
sha: z14.string(),
|
|
3981
|
+
type: z14.nativeEnum(ReferenceType)
|
|
3998
3982
|
});
|
|
3999
3983
|
|
|
4000
3984
|
// src/features/analysis/scm/utils/index.ts
|
|
@@ -4108,7 +4092,7 @@ function shouldValidateUrl(repoUrl) {
|
|
|
4108
4092
|
return repoUrl && isUrlHasPath(repoUrl);
|
|
4109
4093
|
}
|
|
4110
4094
|
function isBrokerUrl(url) {
|
|
4111
|
-
return
|
|
4095
|
+
return z15.string().uuid().safeParse(new URL(url).host).success;
|
|
4112
4096
|
}
|
|
4113
4097
|
function buildAuthorizedRepoUrl(args) {
|
|
4114
4098
|
const { url, username, password } = args;
|
|
@@ -4144,7 +4128,7 @@ function getCloudScmLibTypeFromUrl(url) {
|
|
|
4144
4128
|
return void 0;
|
|
4145
4129
|
}
|
|
4146
4130
|
function getScmLibTypeFromScmType(scmType) {
|
|
4147
|
-
const parsedScmType =
|
|
4131
|
+
const parsedScmType = z15.nativeEnum(ScmType).parse(scmType);
|
|
4148
4132
|
return scmTypeToScmLibScmType[parsedScmType];
|
|
4149
4133
|
}
|
|
4150
4134
|
function getScmConfig({
|
|
@@ -4211,42 +4195,42 @@ function getScmConfig({
|
|
|
4211
4195
|
}
|
|
4212
4196
|
|
|
4213
4197
|
// src/features/analysis/scm/ado/validation.ts
|
|
4214
|
-
import { z as
|
|
4215
|
-
var ValidPullRequestStatusZ =
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4198
|
+
import { z as z16 } from "zod";
|
|
4199
|
+
var ValidPullRequestStatusZ = z16.union([
|
|
4200
|
+
z16.literal(1 /* Active */),
|
|
4201
|
+
z16.literal(2 /* Abandoned */),
|
|
4202
|
+
z16.literal(3 /* Completed */)
|
|
4219
4203
|
]);
|
|
4220
|
-
var AdoAuthResultZ =
|
|
4221
|
-
access_token:
|
|
4222
|
-
token_type:
|
|
4223
|
-
refresh_token:
|
|
4204
|
+
var AdoAuthResultZ = z16.object({
|
|
4205
|
+
access_token: z16.string().min(1),
|
|
4206
|
+
token_type: z16.string().min(1),
|
|
4207
|
+
refresh_token: z16.string().min(1)
|
|
4224
4208
|
});
|
|
4225
4209
|
var AdoAuthResultWithOrgsZ = AdoAuthResultZ.extend({
|
|
4226
|
-
scmOrgs:
|
|
4210
|
+
scmOrgs: z16.array(z16.string())
|
|
4227
4211
|
});
|
|
4228
|
-
var profileZ =
|
|
4229
|
-
displayName:
|
|
4230
|
-
publicAlias:
|
|
4231
|
-
emailAddress:
|
|
4232
|
-
coreRevision:
|
|
4233
|
-
timeStamp:
|
|
4234
|
-
id:
|
|
4235
|
-
revision:
|
|
4212
|
+
var profileZ = z16.object({
|
|
4213
|
+
displayName: z16.string(),
|
|
4214
|
+
publicAlias: z16.string().min(1),
|
|
4215
|
+
emailAddress: z16.string(),
|
|
4216
|
+
coreRevision: z16.number(),
|
|
4217
|
+
timeStamp: z16.string(),
|
|
4218
|
+
id: z16.string(),
|
|
4219
|
+
revision: z16.number()
|
|
4236
4220
|
});
|
|
4237
|
-
var accountsZ =
|
|
4238
|
-
count:
|
|
4239
|
-
value:
|
|
4240
|
-
|
|
4241
|
-
accountId:
|
|
4242
|
-
accountUri:
|
|
4243
|
-
accountName:
|
|
4221
|
+
var accountsZ = z16.object({
|
|
4222
|
+
count: z16.number(),
|
|
4223
|
+
value: z16.array(
|
|
4224
|
+
z16.object({
|
|
4225
|
+
accountId: z16.string(),
|
|
4226
|
+
accountUri: z16.string(),
|
|
4227
|
+
accountName: z16.string()
|
|
4244
4228
|
})
|
|
4245
4229
|
)
|
|
4246
4230
|
});
|
|
4247
4231
|
|
|
4248
4232
|
// src/features/analysis/scm/ado/utils.ts
|
|
4249
|
-
var
|
|
4233
|
+
var debug = Debug("mobbdev:scm:ado");
|
|
4250
4234
|
function _getPublicAdoClient({
|
|
4251
4235
|
orgName,
|
|
4252
4236
|
origin: origin2
|
|
@@ -4325,7 +4309,7 @@ async function getAdoConnectData({
|
|
|
4325
4309
|
oauthToken: adoTokenInfo.accessToken
|
|
4326
4310
|
});
|
|
4327
4311
|
return {
|
|
4328
|
-
org:
|
|
4312
|
+
org: z17.string().parse(org),
|
|
4329
4313
|
origin: DEFUALT_ADO_ORIGIN
|
|
4330
4314
|
};
|
|
4331
4315
|
}
|
|
@@ -4411,7 +4395,7 @@ async function getAdoClientParams(params) {
|
|
|
4411
4395
|
return {
|
|
4412
4396
|
tokenType: "PAT" /* PAT */,
|
|
4413
4397
|
accessToken: adoTokenInfo.accessToken,
|
|
4414
|
-
patTokenOrg:
|
|
4398
|
+
patTokenOrg: z17.string().parse(tokenOrg).toLowerCase(),
|
|
4415
4399
|
origin: origin2,
|
|
4416
4400
|
orgName: org.toLowerCase()
|
|
4417
4401
|
};
|
|
@@ -4579,6 +4563,18 @@ async function getAdoSdk(params) {
|
|
|
4579
4563
|
);
|
|
4580
4564
|
return `${getRepositoryRes.webUrl}/commit/${commitId}`;
|
|
4581
4565
|
},
|
|
4566
|
+
async getAdoBranchCommitsUrl({
|
|
4567
|
+
repoUrl,
|
|
4568
|
+
branch
|
|
4569
|
+
}) {
|
|
4570
|
+
const { repo, projectName } = parseAdoOwnerAndRepo(repoUrl);
|
|
4571
|
+
const git = await api2.getGitApi();
|
|
4572
|
+
const getRepositoryRes = await git.getRepository(
|
|
4573
|
+
decodeURI(repo),
|
|
4574
|
+
projectName ? decodeURI(projectName) : void 0
|
|
4575
|
+
);
|
|
4576
|
+
return `${getRepositoryRes.webUrl}/commits?itemVersion=${branch}`;
|
|
4577
|
+
},
|
|
4582
4578
|
getAdoDownloadUrl({
|
|
4583
4579
|
repoUrl,
|
|
4584
4580
|
branch
|
|
@@ -4587,7 +4583,7 @@ async function getAdoSdk(params) {
|
|
|
4587
4583
|
const url = new URL(repoUrl);
|
|
4588
4584
|
const origin2 = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
|
|
4589
4585
|
const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
4590
|
-
const
|
|
4586
|
+
const path9 = [
|
|
4591
4587
|
prefixPath,
|
|
4592
4588
|
owner,
|
|
4593
4589
|
projectName,
|
|
@@ -4598,7 +4594,7 @@ async function getAdoSdk(params) {
|
|
|
4598
4594
|
"items",
|
|
4599
4595
|
"items"
|
|
4600
4596
|
].filter(Boolean).join("/");
|
|
4601
|
-
return new URL(`${
|
|
4597
|
+
return new URL(`${path9}?${params2}`, origin2).toString();
|
|
4602
4598
|
},
|
|
4603
4599
|
async getAdoBranchList({ repoUrl }) {
|
|
4604
4600
|
try {
|
|
@@ -5080,6 +5076,14 @@ var AdoSCMLib = class extends SCMLib {
|
|
|
5080
5076
|
commitId
|
|
5081
5077
|
});
|
|
5082
5078
|
}
|
|
5079
|
+
async getBranchCommitsUrl(branchName) {
|
|
5080
|
+
this._validateUrl();
|
|
5081
|
+
const adoSdk = await this.getAdoSdk();
|
|
5082
|
+
return adoSdk.getAdoBranchCommitsUrl({
|
|
5083
|
+
repoUrl: this.url,
|
|
5084
|
+
branch: branchName
|
|
5085
|
+
});
|
|
5086
|
+
}
|
|
5083
5087
|
async addCommentToSubmitRequest(scmSubmitRequestId, comment) {
|
|
5084
5088
|
this._validateAccessTokenAndUrl();
|
|
5085
5089
|
const adoSdk = await this.getAdoSdk();
|
|
@@ -5095,34 +5099,34 @@ var AdoSCMLib = class extends SCMLib {
|
|
|
5095
5099
|
import querystring2 from "node:querystring";
|
|
5096
5100
|
import * as bitbucketPkgNode from "bitbucket";
|
|
5097
5101
|
import bitbucketPkg from "bitbucket";
|
|
5098
|
-
import
|
|
5099
|
-
import { z as
|
|
5102
|
+
import Debug2 from "debug";
|
|
5103
|
+
import { z as z19 } from "zod";
|
|
5100
5104
|
|
|
5101
5105
|
// src/features/analysis/scm/bitbucket/validation.ts
|
|
5102
|
-
import { z as
|
|
5103
|
-
var BitbucketAuthResultZ =
|
|
5104
|
-
access_token:
|
|
5105
|
-
token_type:
|
|
5106
|
-
refresh_token:
|
|
5106
|
+
import { z as z18 } from "zod";
|
|
5107
|
+
var BitbucketAuthResultZ = z18.object({
|
|
5108
|
+
access_token: z18.string(),
|
|
5109
|
+
token_type: z18.string(),
|
|
5110
|
+
refresh_token: z18.string()
|
|
5107
5111
|
});
|
|
5108
5112
|
|
|
5109
5113
|
// src/features/analysis/scm/bitbucket/bitbucket.ts
|
|
5110
|
-
var
|
|
5114
|
+
var debug2 = Debug2("scm:bitbucket");
|
|
5111
5115
|
var BITBUCKET_HOSTNAME = "bitbucket.org";
|
|
5112
|
-
var TokenExpiredErrorZ =
|
|
5113
|
-
status:
|
|
5114
|
-
error:
|
|
5115
|
-
type:
|
|
5116
|
-
error:
|
|
5117
|
-
message:
|
|
5116
|
+
var TokenExpiredErrorZ = z19.object({
|
|
5117
|
+
status: z19.number(),
|
|
5118
|
+
error: z19.object({
|
|
5119
|
+
type: z19.string(),
|
|
5120
|
+
error: z19.object({
|
|
5121
|
+
message: z19.string()
|
|
5118
5122
|
})
|
|
5119
5123
|
})
|
|
5120
5124
|
});
|
|
5121
5125
|
var BITBUCKET_ACCESS_TOKEN_URL = `https://${BITBUCKET_HOSTNAME}/site/oauth2/access_token`;
|
|
5122
|
-
var BitbucketParseResultZ =
|
|
5123
|
-
organization:
|
|
5124
|
-
repoName:
|
|
5125
|
-
hostname:
|
|
5126
|
+
var BitbucketParseResultZ = z19.object({
|
|
5127
|
+
organization: z19.string(),
|
|
5128
|
+
repoName: z19.string(),
|
|
5129
|
+
hostname: z19.literal(BITBUCKET_HOSTNAME)
|
|
5126
5130
|
});
|
|
5127
5131
|
function parseBitbucketOrganizationAndRepo(bitbucketUrl) {
|
|
5128
5132
|
const parsedGitHubUrl = normalizeUrl(bitbucketUrl);
|
|
@@ -5183,7 +5187,7 @@ function getBitbucketSdk(params) {
|
|
|
5183
5187
|
if (!res.data.values) {
|
|
5184
5188
|
return [];
|
|
5185
5189
|
}
|
|
5186
|
-
return res.data.values.filter((branch) => !!branch.name).map((branch) =>
|
|
5190
|
+
return res.data.values.filter((branch) => !!branch.name).map((branch) => z19.string().parse(branch.name));
|
|
5187
5191
|
},
|
|
5188
5192
|
async getIsUserCollaborator(params2) {
|
|
5189
5193
|
const { repoUrl } = params2;
|
|
@@ -5298,7 +5302,7 @@ function getBitbucketSdk(params) {
|
|
|
5298
5302
|
return GetRefererenceResultZ.parse({
|
|
5299
5303
|
sha: tagRes.data.target?.hash,
|
|
5300
5304
|
type: "TAG" /* TAG */,
|
|
5301
|
-
date: new Date(
|
|
5305
|
+
date: new Date(z19.string().parse(tagRes.data.target?.date))
|
|
5302
5306
|
});
|
|
5303
5307
|
},
|
|
5304
5308
|
async getBranchRef(params2) {
|
|
@@ -5306,7 +5310,7 @@ function getBitbucketSdk(params) {
|
|
|
5306
5310
|
return GetRefererenceResultZ.parse({
|
|
5307
5311
|
sha: getBranchRes.target?.hash,
|
|
5308
5312
|
type: "BRANCH" /* BRANCH */,
|
|
5309
|
-
date: new Date(
|
|
5313
|
+
date: new Date(z19.string().parse(getBranchRes.target?.date))
|
|
5310
5314
|
});
|
|
5311
5315
|
},
|
|
5312
5316
|
async getCommitRef(params2) {
|
|
@@ -5314,13 +5318,13 @@ function getBitbucketSdk(params) {
|
|
|
5314
5318
|
return GetRefererenceResultZ.parse({
|
|
5315
5319
|
sha: getCommitRes.hash,
|
|
5316
5320
|
type: "COMMIT" /* COMMIT */,
|
|
5317
|
-
date: new Date(
|
|
5321
|
+
date: new Date(z19.string().parse(getCommitRes.date))
|
|
5318
5322
|
});
|
|
5319
5323
|
},
|
|
5320
5324
|
async getDownloadUrl({ url, sha }) {
|
|
5321
5325
|
this.getReferenceData({ ref: sha, url });
|
|
5322
5326
|
const repoRes = await this.getRepo({ repoUrl: url });
|
|
5323
|
-
const parsedRepoUrl =
|
|
5327
|
+
const parsedRepoUrl = z19.string().url().parse(repoRes.links?.html?.href);
|
|
5324
5328
|
return `${parsedRepoUrl}/get/${sha}.zip`;
|
|
5325
5329
|
},
|
|
5326
5330
|
async getPullRequest(params2) {
|
|
@@ -5385,7 +5389,7 @@ async function validateBitbucketParams(params) {
|
|
|
5385
5389
|
}
|
|
5386
5390
|
async function getUsersworkspacesSlugs(bitbucketClient) {
|
|
5387
5391
|
const res = await bitbucketClient.workspaces.getWorkspaces({});
|
|
5388
|
-
return res.data.values?.map((v) =>
|
|
5392
|
+
return res.data.values?.map((v) => z19.string().parse(v.slug));
|
|
5389
5393
|
}
|
|
5390
5394
|
async function getllUsersrepositories(bitbucketClient) {
|
|
5391
5395
|
const userWorspacesSlugs = await getUsersworkspacesSlugs(bitbucketClient);
|
|
@@ -5413,10 +5417,10 @@ async function getRepositoriesByWorkspace(bitbucketClient, { workspaceSlug }) {
|
|
|
5413
5417
|
|
|
5414
5418
|
// src/features/analysis/scm/bitbucket/BitbucketSCMLib.ts
|
|
5415
5419
|
import { setTimeout as setTimeout3 } from "node:timers/promises";
|
|
5416
|
-
import { z as
|
|
5420
|
+
import { z as z20 } from "zod";
|
|
5417
5421
|
function getUserAndPassword(token) {
|
|
5418
5422
|
const [username, password] = token.split(":");
|
|
5419
|
-
const safePasswordAndUsername =
|
|
5423
|
+
const safePasswordAndUsername = z20.object({ username: z20.string(), password: z20.string() }).parse({ username, password });
|
|
5420
5424
|
return {
|
|
5421
5425
|
username: safePasswordAndUsername.username,
|
|
5422
5426
|
password: safePasswordAndUsername.password
|
|
@@ -5488,7 +5492,7 @@ var BitbucketSCMLib = class extends SCMLib {
|
|
|
5488
5492
|
return { username, password, authType };
|
|
5489
5493
|
}
|
|
5490
5494
|
case "token": {
|
|
5491
|
-
return { authType, token:
|
|
5495
|
+
return { authType, token: z20.string().parse(this.accessToken) };
|
|
5492
5496
|
}
|
|
5493
5497
|
case "public":
|
|
5494
5498
|
return { authType };
|
|
@@ -5502,7 +5506,7 @@ var BitbucketSCMLib = class extends SCMLib {
|
|
|
5502
5506
|
...params,
|
|
5503
5507
|
repoUrl: this.url
|
|
5504
5508
|
});
|
|
5505
|
-
return String(
|
|
5509
|
+
return String(z20.number().parse(pullRequestRes.id));
|
|
5506
5510
|
} catch (e) {
|
|
5507
5511
|
console.warn(
|
|
5508
5512
|
`error creating pull request for BB. Try number ${i + 1}`,
|
|
@@ -5587,7 +5591,7 @@ var BitbucketSCMLib = class extends SCMLib {
|
|
|
5587
5591
|
async getUsername() {
|
|
5588
5592
|
this._validateAccessToken();
|
|
5589
5593
|
const res = await this.bitbucketSdk.getUser();
|
|
5590
|
-
return
|
|
5594
|
+
return z20.string().parse(res.username);
|
|
5591
5595
|
}
|
|
5592
5596
|
async getSubmitRequestStatus(_scmSubmitRequestId) {
|
|
5593
5597
|
this._validateAccessTokenAndUrl();
|
|
@@ -5616,7 +5620,7 @@ var BitbucketSCMLib = class extends SCMLib {
|
|
|
5616
5620
|
async getRepoDefaultBranch() {
|
|
5617
5621
|
this._validateUrl();
|
|
5618
5622
|
const repoRes = await this.bitbucketSdk.getRepo({ repoUrl: this.url });
|
|
5619
|
-
return
|
|
5623
|
+
return z20.string().parse(repoRes.mainbranch?.name);
|
|
5620
5624
|
}
|
|
5621
5625
|
getSubmitRequestUrl(submitRequestId) {
|
|
5622
5626
|
this._validateUrl();
|
|
@@ -5636,6 +5640,13 @@ var BitbucketSCMLib = class extends SCMLib {
|
|
|
5636
5640
|
`https://bitbucket.org/${workspace}/${repo_slug}/commits/${commitId}`
|
|
5637
5641
|
);
|
|
5638
5642
|
}
|
|
5643
|
+
getBranchCommitsUrl(branchName) {
|
|
5644
|
+
this._validateUrl();
|
|
5645
|
+
const { repo_slug, workspace } = parseBitbucketOrganizationAndRepo(this.url);
|
|
5646
|
+
return Promise.resolve(
|
|
5647
|
+
`https://bitbucket.org/${workspace}/${repo_slug}/branch/${branchName}`
|
|
5648
|
+
);
|
|
5649
|
+
}
|
|
5639
5650
|
async addCommentToSubmitRequest(submitRequestId, comment) {
|
|
5640
5651
|
this._validateUrl();
|
|
5641
5652
|
await this.bitbucketSdk.addCommentToPullRequest({
|
|
@@ -5651,7 +5662,7 @@ var MOBB_ICON_IMG = "https://app.mobb.ai/gh-action/Logo_Rounded_Icon.svg";
|
|
|
5651
5662
|
var MAX_BRANCHES_FETCH = 1e3;
|
|
5652
5663
|
|
|
5653
5664
|
// src/features/analysis/scm/github/GithubSCMLib.ts
|
|
5654
|
-
import { z as
|
|
5665
|
+
import { z as z21 } from "zod";
|
|
5655
5666
|
|
|
5656
5667
|
// src/features/analysis/scm/github/github.ts
|
|
5657
5668
|
import { RequestError } from "@octokit/request-error";
|
|
@@ -6040,14 +6051,14 @@ function getGithubSdk(params = {}) {
|
|
|
6040
6051
|
};
|
|
6041
6052
|
},
|
|
6042
6053
|
async getGithubBlameRanges(params2) {
|
|
6043
|
-
const { ref, gitHubUrl, path:
|
|
6054
|
+
const { ref, gitHubUrl, path: path9 } = params2;
|
|
6044
6055
|
const { owner, repo } = parseGithubOwnerAndRepo(gitHubUrl);
|
|
6045
6056
|
const res = await octokit.graphql(
|
|
6046
6057
|
GET_BLAME_DOCUMENT,
|
|
6047
6058
|
{
|
|
6048
6059
|
owner,
|
|
6049
6060
|
repo,
|
|
6050
|
-
path:
|
|
6061
|
+
path: path9,
|
|
6051
6062
|
ref
|
|
6052
6063
|
}
|
|
6053
6064
|
);
|
|
@@ -6289,7 +6300,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6289
6300
|
owner,
|
|
6290
6301
|
repo
|
|
6291
6302
|
});
|
|
6292
|
-
return
|
|
6303
|
+
return z21.string().parse(prRes.data);
|
|
6293
6304
|
}
|
|
6294
6305
|
async getRepoList(_scmOrg) {
|
|
6295
6306
|
this._validateAccessToken();
|
|
@@ -6353,11 +6364,11 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6353
6364
|
markdownComment: comment
|
|
6354
6365
|
});
|
|
6355
6366
|
}
|
|
6356
|
-
async getRepoBlameRanges(ref,
|
|
6367
|
+
async getRepoBlameRanges(ref, path9) {
|
|
6357
6368
|
this._validateUrl();
|
|
6358
6369
|
return await this.githubSdk.getGithubBlameRanges({
|
|
6359
6370
|
ref,
|
|
6360
|
-
path:
|
|
6371
|
+
path: path9,
|
|
6361
6372
|
gitHubUrl: this.url
|
|
6362
6373
|
});
|
|
6363
6374
|
}
|
|
@@ -6402,6 +6413,10 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
6402
6413
|
});
|
|
6403
6414
|
return getCommitRes.data.html_url;
|
|
6404
6415
|
}
|
|
6416
|
+
async getBranchCommitsUrl(branchName) {
|
|
6417
|
+
this._validateAccessTokenAndUrl();
|
|
6418
|
+
return `${this.url}/commits/${branchName}`;
|
|
6419
|
+
}
|
|
6405
6420
|
async postGeneralPrComment(params) {
|
|
6406
6421
|
const { prNumber, body } = params;
|
|
6407
6422
|
this._validateAccessTokenAndUrl();
|
|
@@ -6445,22 +6460,22 @@ import {
|
|
|
6445
6460
|
AccessLevel,
|
|
6446
6461
|
Gitlab
|
|
6447
6462
|
} from "@gitbeaker/rest";
|
|
6448
|
-
import
|
|
6463
|
+
import Debug3 from "debug";
|
|
6449
6464
|
import {
|
|
6450
6465
|
fetch as undiciFetch,
|
|
6451
6466
|
ProxyAgent as ProxyAgent2
|
|
6452
6467
|
} from "undici";
|
|
6453
6468
|
|
|
6454
6469
|
// src/features/analysis/scm/gitlab/types.ts
|
|
6455
|
-
import { z as
|
|
6456
|
-
var GitlabAuthResultZ =
|
|
6457
|
-
access_token:
|
|
6458
|
-
token_type:
|
|
6459
|
-
refresh_token:
|
|
6470
|
+
import { z as z22 } from "zod";
|
|
6471
|
+
var GitlabAuthResultZ = z22.object({
|
|
6472
|
+
access_token: z22.string(),
|
|
6473
|
+
token_type: z22.string(),
|
|
6474
|
+
refresh_token: z22.string()
|
|
6460
6475
|
});
|
|
6461
6476
|
|
|
6462
6477
|
// src/features/analysis/scm/gitlab/gitlab.ts
|
|
6463
|
-
var
|
|
6478
|
+
var debug3 = Debug3("scm:gitlab");
|
|
6464
6479
|
function removeTrailingSlash2(str) {
|
|
6465
6480
|
return str.trim().replace(/\/+$/, "");
|
|
6466
6481
|
}
|
|
@@ -6755,13 +6770,13 @@ function parseGitlabOwnerAndRepo(gitlabUrl) {
|
|
|
6755
6770
|
const { organization, repoName, projectPath } = parsingResult;
|
|
6756
6771
|
return { owner: organization, repo: repoName, projectPath };
|
|
6757
6772
|
}
|
|
6758
|
-
async function getGitlabBlameRanges({ ref, gitlabUrl, path:
|
|
6773
|
+
async function getGitlabBlameRanges({ ref, gitlabUrl, path: path9 }, options) {
|
|
6759
6774
|
const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
|
|
6760
6775
|
const api2 = getGitBeaker({
|
|
6761
6776
|
url: gitlabUrl,
|
|
6762
6777
|
gitlabAuthToken: options?.gitlabAuthToken
|
|
6763
6778
|
});
|
|
6764
|
-
const resp = await api2.RepositoryFiles.allFileBlames(projectPath,
|
|
6779
|
+
const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path9, ref);
|
|
6765
6780
|
let lineNumber = 1;
|
|
6766
6781
|
return resp.filter((range) => range.lines).map((range) => {
|
|
6767
6782
|
const oldLineNumber = lineNumber;
|
|
@@ -6937,10 +6952,10 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
6937
6952
|
markdownComment: comment
|
|
6938
6953
|
});
|
|
6939
6954
|
}
|
|
6940
|
-
async getRepoBlameRanges(ref,
|
|
6955
|
+
async getRepoBlameRanges(ref, path9) {
|
|
6941
6956
|
this._validateUrl();
|
|
6942
6957
|
return await getGitlabBlameRanges(
|
|
6943
|
-
{ ref, path:
|
|
6958
|
+
{ ref, path: path9, gitlabUrl: this.url },
|
|
6944
6959
|
{
|
|
6945
6960
|
url: this.url,
|
|
6946
6961
|
gitlabAuthToken: this.accessToken
|
|
@@ -6986,141 +7001,677 @@ var GitlabSCMLib = class extends SCMLib {
|
|
|
6986
7001
|
});
|
|
6987
7002
|
return res.web_url;
|
|
6988
7003
|
}
|
|
7004
|
+
async getBranchCommitsUrl(branchName) {
|
|
7005
|
+
this._validateAccessTokenAndUrl();
|
|
7006
|
+
return `${this.url}/-/commits/${branchName}`;
|
|
7007
|
+
}
|
|
7008
|
+
};
|
|
7009
|
+
|
|
7010
|
+
// src/features/analysis/scm/scmFactory.ts
|
|
7011
|
+
import { z as z23 } from "zod";
|
|
7012
|
+
|
|
7013
|
+
// src/features/analysis/scm/StubSCMLib.ts
|
|
7014
|
+
var StubSCMLib = class extends SCMLib {
|
|
7015
|
+
constructor(url, accessToken, scmOrg) {
|
|
7016
|
+
super(url, accessToken, scmOrg);
|
|
7017
|
+
}
|
|
7018
|
+
async getUrlWithCredentials() {
|
|
7019
|
+
console.warn("getUrlWithCredentials() returning empty string");
|
|
7020
|
+
return "";
|
|
7021
|
+
}
|
|
7022
|
+
async createSubmitRequest(_params) {
|
|
7023
|
+
console.warn("createSubmitRequest() returning empty string");
|
|
7024
|
+
return "";
|
|
7025
|
+
}
|
|
7026
|
+
get scmLibType() {
|
|
7027
|
+
console.warn("scmLibType returning GITHUB as default");
|
|
7028
|
+
return "GITHUB" /* GITHUB */;
|
|
7029
|
+
}
|
|
7030
|
+
getAuthHeaders() {
|
|
7031
|
+
console.warn("getAuthHeaders() returning empty object");
|
|
7032
|
+
return {};
|
|
7033
|
+
}
|
|
7034
|
+
async getDownloadUrl(_sha) {
|
|
7035
|
+
console.warn("getDownloadUrl() returning empty string");
|
|
7036
|
+
return "";
|
|
7037
|
+
}
|
|
7038
|
+
async getIsRemoteBranch(_branch) {
|
|
7039
|
+
console.warn("getIsRemoteBranch() returning false");
|
|
7040
|
+
return false;
|
|
7041
|
+
}
|
|
7042
|
+
async validateParams() {
|
|
7043
|
+
console.warn("validateParams() no-op");
|
|
7044
|
+
}
|
|
7045
|
+
async getRepoList(_scmOrg) {
|
|
7046
|
+
console.warn("getRepoList() returning empty array");
|
|
7047
|
+
return [];
|
|
7048
|
+
}
|
|
7049
|
+
async getBranchList() {
|
|
7050
|
+
console.warn("getBranchList() returning empty array");
|
|
7051
|
+
return [];
|
|
7052
|
+
}
|
|
7053
|
+
async getUsername() {
|
|
7054
|
+
console.warn("getUsername() returning empty string");
|
|
7055
|
+
return "";
|
|
7056
|
+
}
|
|
7057
|
+
async getSubmitRequestStatus(_scmSubmitRequestId) {
|
|
7058
|
+
console.warn("getSubmitRequestStatus() returning ERROR");
|
|
7059
|
+
return "error";
|
|
7060
|
+
}
|
|
7061
|
+
async getUserHasAccessToRepo() {
|
|
7062
|
+
console.warn("getUserHasAccessToRepo() returning false");
|
|
7063
|
+
return false;
|
|
7064
|
+
}
|
|
7065
|
+
async getRepoBlameRanges(_ref, _path) {
|
|
7066
|
+
console.warn("getRepoBlameRanges() returning empty array");
|
|
7067
|
+
return [];
|
|
7068
|
+
}
|
|
7069
|
+
async getReferenceData(_ref) {
|
|
7070
|
+
console.warn("getReferenceData() returning null/empty defaults");
|
|
7071
|
+
return {
|
|
7072
|
+
type: "BRANCH" /* BRANCH */,
|
|
7073
|
+
sha: "",
|
|
7074
|
+
date: void 0
|
|
7075
|
+
};
|
|
7076
|
+
}
|
|
7077
|
+
async getRepoDefaultBranch() {
|
|
7078
|
+
console.warn("getRepoDefaultBranch() returning empty string");
|
|
7079
|
+
return "";
|
|
7080
|
+
}
|
|
7081
|
+
async getSubmitRequestUrl(_submitRequestIdNumber) {
|
|
7082
|
+
console.warn("getSubmitRequestUrl() returning empty string");
|
|
7083
|
+
return "";
|
|
7084
|
+
}
|
|
7085
|
+
async getSubmitRequestId(_submitRequestUrl) {
|
|
7086
|
+
console.warn("getSubmitRequestId() returning empty string");
|
|
7087
|
+
return "";
|
|
7088
|
+
}
|
|
7089
|
+
async getCommitUrl(_commitId) {
|
|
7090
|
+
console.warn("getCommitUrl() returning empty string");
|
|
7091
|
+
return "";
|
|
7092
|
+
}
|
|
7093
|
+
async getBranchCommitsUrl(_branchName) {
|
|
7094
|
+
console.warn("getBranchCommitsUrl() returning empty string");
|
|
7095
|
+
return "";
|
|
7096
|
+
}
|
|
7097
|
+
async _getUsernameForAuthUrl() {
|
|
7098
|
+
console.warn("_getUsernameForAuthUrl() returning empty string");
|
|
7099
|
+
return "";
|
|
7100
|
+
}
|
|
7101
|
+
async addCommentToSubmitRequest(_submitRequestId, _comment) {
|
|
7102
|
+
console.warn("addCommentToSubmitRequest() no-op");
|
|
7103
|
+
}
|
|
7104
|
+
};
|
|
7105
|
+
|
|
7106
|
+
// src/features/analysis/scm/scmFactory.ts
|
|
7107
|
+
async function createScmLib({ url, accessToken, scmType, scmOrg }, { propagateExceptions = false } = {}) {
|
|
7108
|
+
const trimmedUrl = url ? url.trim().replace(/\/$/, "").replace(/.git$/i, "") : void 0;
|
|
7109
|
+
try {
|
|
7110
|
+
switch (scmType) {
|
|
7111
|
+
case "GITHUB" /* GITHUB */: {
|
|
7112
|
+
const scm = new GithubSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
7113
|
+
await scm.validateParams();
|
|
7114
|
+
return scm;
|
|
7115
|
+
}
|
|
7116
|
+
case "GITLAB" /* GITLAB */: {
|
|
7117
|
+
const scm = new GitlabSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
7118
|
+
await scm.validateParams();
|
|
7119
|
+
return scm;
|
|
7120
|
+
}
|
|
7121
|
+
case "ADO" /* ADO */: {
|
|
7122
|
+
const scm = new AdoSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
7123
|
+
await scm.getAdoSdk();
|
|
7124
|
+
await scm.validateParams();
|
|
7125
|
+
return scm;
|
|
7126
|
+
}
|
|
7127
|
+
case "BITBUCKET" /* BITBUCKET */: {
|
|
7128
|
+
const scm = new BitbucketSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
7129
|
+
await scm.validateParams();
|
|
7130
|
+
return scm;
|
|
7131
|
+
}
|
|
7132
|
+
}
|
|
7133
|
+
} catch (e) {
|
|
7134
|
+
if (e instanceof InvalidRepoUrlError && url) {
|
|
7135
|
+
throw new RepoNoTokenAccessError(
|
|
7136
|
+
"no access to repo",
|
|
7137
|
+
scmLibScmTypeToScmType[z23.nativeEnum(ScmLibScmType).parse(scmType)]
|
|
7138
|
+
);
|
|
7139
|
+
}
|
|
7140
|
+
console.error(`error validating scm: ${scmType} `, e);
|
|
7141
|
+
if (propagateExceptions) {
|
|
7142
|
+
throw e;
|
|
7143
|
+
}
|
|
7144
|
+
}
|
|
7145
|
+
return new StubSCMLib(trimmedUrl, void 0, void 0);
|
|
7146
|
+
}
|
|
7147
|
+
|
|
7148
|
+
// src/utils/index.ts
|
|
7149
|
+
var utils_exports = {};
|
|
7150
|
+
__export(utils_exports, {
|
|
7151
|
+
CliError: () => CliError,
|
|
7152
|
+
Spinner: () => Spinner,
|
|
7153
|
+
getDirName: () => getDirName,
|
|
7154
|
+
getTopLevelDirName: () => getTopLevelDirName,
|
|
7155
|
+
keypress: () => keypress,
|
|
7156
|
+
packageJson: () => packageJson,
|
|
7157
|
+
sleep: () => sleep
|
|
7158
|
+
});
|
|
7159
|
+
|
|
7160
|
+
// src/utils/dirname.ts
|
|
7161
|
+
import path from "node:path";
|
|
7162
|
+
import { fileURLToPath } from "node:url";
|
|
7163
|
+
function getDirName() {
|
|
7164
|
+
return path.dirname(fileURLToPath(import.meta.url));
|
|
7165
|
+
}
|
|
7166
|
+
function getTopLevelDirName(fullPath) {
|
|
7167
|
+
return path.parse(fullPath).name;
|
|
7168
|
+
}
|
|
7169
|
+
|
|
7170
|
+
// src/utils/keypress.ts
|
|
7171
|
+
import readline from "node:readline";
|
|
7172
|
+
async function keypress() {
|
|
7173
|
+
const rl = readline.createInterface({
|
|
7174
|
+
input: process.stdin,
|
|
7175
|
+
output: process.stdout
|
|
7176
|
+
});
|
|
7177
|
+
return new Promise((resolve) => {
|
|
7178
|
+
rl.question("", (answer) => {
|
|
7179
|
+
rl.close();
|
|
7180
|
+
process.stderr.moveCursor(0, -1);
|
|
7181
|
+
process.stderr.clearLine(1);
|
|
7182
|
+
resolve(answer);
|
|
7183
|
+
});
|
|
7184
|
+
});
|
|
7185
|
+
}
|
|
7186
|
+
|
|
7187
|
+
// src/utils/spinner.ts
|
|
7188
|
+
import {
|
|
7189
|
+
createSpinner as _createSpinner
|
|
7190
|
+
} from "nanospinner";
|
|
7191
|
+
function printToStdError(opts) {
|
|
7192
|
+
if (opts?.text) console.error(opts.text);
|
|
7193
|
+
}
|
|
7194
|
+
var mockSpinner = {
|
|
7195
|
+
success: (opts) => {
|
|
7196
|
+
printToStdError(opts);
|
|
7197
|
+
return mockSpinner;
|
|
7198
|
+
},
|
|
7199
|
+
error: (opts) => {
|
|
7200
|
+
printToStdError(opts);
|
|
7201
|
+
return mockSpinner;
|
|
7202
|
+
},
|
|
7203
|
+
warn: (opts) => {
|
|
7204
|
+
printToStdError(opts);
|
|
7205
|
+
return mockSpinner;
|
|
7206
|
+
},
|
|
7207
|
+
stop: (opts) => {
|
|
7208
|
+
printToStdError(opts);
|
|
7209
|
+
return mockSpinner;
|
|
7210
|
+
},
|
|
7211
|
+
start: (opts) => {
|
|
7212
|
+
printToStdError(opts);
|
|
7213
|
+
return mockSpinner;
|
|
7214
|
+
},
|
|
7215
|
+
update: (opts) => {
|
|
7216
|
+
printToStdError(opts);
|
|
7217
|
+
return mockSpinner;
|
|
7218
|
+
},
|
|
7219
|
+
reset: () => mockSpinner,
|
|
7220
|
+
clear: () => mockSpinner,
|
|
7221
|
+
spin: () => mockSpinner
|
|
7222
|
+
};
|
|
7223
|
+
function Spinner({ ci = false } = {}) {
|
|
7224
|
+
return {
|
|
7225
|
+
createSpinner: (text, options) => ci ? mockSpinner : _createSpinner(text, options)
|
|
7226
|
+
};
|
|
7227
|
+
}
|
|
7228
|
+
|
|
7229
|
+
// src/utils/check_node_version.ts
|
|
7230
|
+
import fs2 from "node:fs";
|
|
7231
|
+
import path2 from "node:path";
|
|
7232
|
+
import semver from "semver";
|
|
7233
|
+
function getPackageJson() {
|
|
7234
|
+
let manifestPath = path2.join(getDirName(), "../package.json");
|
|
7235
|
+
if (!fs2.existsSync(manifestPath)) {
|
|
7236
|
+
manifestPath = path2.join(getDirName(), "../../package.json");
|
|
7237
|
+
}
|
|
7238
|
+
return JSON.parse(fs2.readFileSync(manifestPath, "utf8"));
|
|
7239
|
+
}
|
|
7240
|
+
var packageJson = getPackageJson();
|
|
7241
|
+
if (!semver.satisfies(process.version, packageJson.engines.node)) {
|
|
7242
|
+
console.error(
|
|
7243
|
+
`
|
|
7244
|
+
\u26A0\uFE0F ${packageJson.name} requires node version ${packageJson.engines.node}, but running ${process.version}.`
|
|
7245
|
+
);
|
|
7246
|
+
process.exit(1);
|
|
7247
|
+
}
|
|
7248
|
+
|
|
7249
|
+
// src/utils/index.ts
|
|
7250
|
+
var sleep = (ms = 2e3) => new Promise((r) => setTimeout(r, ms));
|
|
7251
|
+
var CliError = class extends Error {
|
|
7252
|
+
};
|
|
7253
|
+
|
|
7254
|
+
// src/commands/convert_to_sarif.ts
|
|
7255
|
+
import AdmZip from "adm-zip";
|
|
7256
|
+
import multimatch from "multimatch";
|
|
7257
|
+
import tmp from "tmp";
|
|
7258
|
+
async function convertToSarif(options) {
|
|
7259
|
+
switch (options.inputFileFormat) {
|
|
7260
|
+
case "FortifyFPR" /* FortifyFPR */:
|
|
7261
|
+
await convertFprToSarif(
|
|
7262
|
+
options.inputFilePath,
|
|
7263
|
+
options.outputFilePath,
|
|
7264
|
+
options.codePathPatterns ?? ["**", "*"]
|
|
7265
|
+
);
|
|
7266
|
+
break;
|
|
7267
|
+
}
|
|
7268
|
+
}
|
|
7269
|
+
async function convertFprToSarif(inputFilePath, outputFilePath, codePathPatterns) {
|
|
7270
|
+
const zipIn = new AdmZip(inputFilePath);
|
|
7271
|
+
if (!zipIn.getEntry("audit.fvdl")) {
|
|
7272
|
+
throw new CliError(
|
|
7273
|
+
"\nError: the input file should be in a valid Fortify FPR format."
|
|
7274
|
+
);
|
|
7275
|
+
}
|
|
7276
|
+
const tmpObj = tmp.dirSync({
|
|
7277
|
+
unsafeCleanup: true
|
|
7278
|
+
});
|
|
7279
|
+
try {
|
|
7280
|
+
zipIn.extractEntryTo("audit.fvdl", tmpObj.name);
|
|
7281
|
+
const auditFvdlSaxParser = initSaxParser(
|
|
7282
|
+
path3.join(tmpObj.name, "audit.fvdl")
|
|
7283
|
+
);
|
|
7284
|
+
const vulnerabilityParser = new VulnerabilityParser(
|
|
7285
|
+
auditFvdlSaxParser.parser
|
|
7286
|
+
);
|
|
7287
|
+
const unifiedNodePoolParser = new UnifiedNodePoolParser(
|
|
7288
|
+
auditFvdlSaxParser.parser
|
|
7289
|
+
);
|
|
7290
|
+
const reportMetadataParser = new ReportMetadataParser(
|
|
7291
|
+
auditFvdlSaxParser.parser
|
|
7292
|
+
);
|
|
7293
|
+
let auditMetadataParser = null;
|
|
7294
|
+
await auditFvdlSaxParser.parse();
|
|
7295
|
+
if (zipIn.getEntry("audit.xml")) {
|
|
7296
|
+
zipIn.extractEntryTo("audit.xml", tmpObj.name);
|
|
7297
|
+
const auditXmlSaxParser = initSaxParser(
|
|
7298
|
+
path3.join(tmpObj.name, "audit.xml")
|
|
7299
|
+
);
|
|
7300
|
+
auditMetadataParser = new AuditMetadataParser(auditXmlSaxParser.parser);
|
|
7301
|
+
await auditXmlSaxParser.parse();
|
|
7302
|
+
}
|
|
7303
|
+
fs3.writeFileSync(
|
|
7304
|
+
outputFilePath,
|
|
7305
|
+
`{
|
|
7306
|
+
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
|
7307
|
+
"version": "2.1.0",
|
|
7308
|
+
"runs": [
|
|
7309
|
+
{
|
|
7310
|
+
"tool": {
|
|
7311
|
+
"driver": {
|
|
7312
|
+
"name": "Fortify Mobb Converter"
|
|
7313
|
+
}
|
|
7314
|
+
},
|
|
7315
|
+
"results": [
|
|
7316
|
+
`
|
|
7317
|
+
);
|
|
7318
|
+
vulnerabilityParser.getVulnerabilities().map(
|
|
7319
|
+
(vulnerability) => fortifyVulnerabilityToSarifResult(
|
|
7320
|
+
vulnerability,
|
|
7321
|
+
auditMetadataParser,
|
|
7322
|
+
reportMetadataParser,
|
|
7323
|
+
unifiedNodePoolParser
|
|
7324
|
+
)
|
|
7325
|
+
).filter((sarifResult) => filterSarifResult(sarifResult, codePathPatterns)).forEach((sarifResult, index) => {
|
|
7326
|
+
fs3.appendFileSync(outputFilePath, JSON.stringify(sarifResult, null, 2));
|
|
7327
|
+
if (index !== vulnerabilityParser.getVulnerabilities().length - 1) {
|
|
7328
|
+
fs3.appendFileSync(outputFilePath, ",\n");
|
|
7329
|
+
}
|
|
7330
|
+
});
|
|
7331
|
+
fs3.appendFileSync(outputFilePath, "\n]}]}");
|
|
7332
|
+
} finally {
|
|
7333
|
+
tmpObj.removeCallback();
|
|
7334
|
+
}
|
|
7335
|
+
}
|
|
7336
|
+
function filterSarifResult(sarifResult, codePathPatterns) {
|
|
7337
|
+
const paths = sarifResult.locations.map(
|
|
7338
|
+
(l) => l.physicalLocation.artifactLocation.uri
|
|
7339
|
+
);
|
|
7340
|
+
const matchPaths = multimatch(paths, codePathPatterns, {
|
|
7341
|
+
dot: true
|
|
7342
|
+
});
|
|
7343
|
+
return matchPaths.length > 0;
|
|
7344
|
+
}
|
|
7345
|
+
function fortifyVulnerabilityToSarifResult(vulnerability, auditMetadataParser, reportMetadataParser, unifiedNodePoolParser) {
|
|
7346
|
+
const suppressed = auditMetadataParser?.getAuditMetadata()[vulnerability.instanceID] || "false";
|
|
7347
|
+
const ruleMeta = reportMetadataParser.getReportMetadata().rules[vulnerability.classID] ?? {};
|
|
7348
|
+
return {
|
|
7349
|
+
ruleId: vulnerability.type + (vulnerability.subtype ? `: ${vulnerability.subtype}` : ""),
|
|
7350
|
+
properties: {
|
|
7351
|
+
package: ruleMeta["package"] ?? "",
|
|
7352
|
+
probability: vulnerability.metaInfo["Probability"] ?? ruleMeta["Probability"] ?? "",
|
|
7353
|
+
impact: vulnerability.metaInfo["Impact"] ?? ruleMeta["Impact"] ?? "",
|
|
7354
|
+
accuracy: vulnerability.metaInfo["Accuracy"] ?? ruleMeta["Accuracy"] ?? "",
|
|
7355
|
+
confidence: vulnerability.confidence,
|
|
7356
|
+
instanceID: vulnerability.instanceID,
|
|
7357
|
+
instanceSeverity: vulnerability.instanceSeverity,
|
|
7358
|
+
suppressed
|
|
7359
|
+
},
|
|
7360
|
+
locations: fortifyNodesToSarifLocations(
|
|
7361
|
+
vulnerability.nodes,
|
|
7362
|
+
unifiedNodePoolParser
|
|
7363
|
+
),
|
|
7364
|
+
message: {
|
|
7365
|
+
text: "no message"
|
|
7366
|
+
}
|
|
7367
|
+
};
|
|
7368
|
+
}
|
|
7369
|
+
function fortifyNodesToSarifLocations(nodes, unifiedNodePoolParser) {
|
|
7370
|
+
return nodes.map((node) => {
|
|
7371
|
+
let sourceLocation;
|
|
7372
|
+
if ("id" in node) {
|
|
7373
|
+
const poolNode = unifiedNodePoolParser.getNodesPull()[node.id];
|
|
7374
|
+
if (poolNode) {
|
|
7375
|
+
sourceLocation = poolNode;
|
|
7376
|
+
} else {
|
|
7377
|
+
throw new CliError(
|
|
7378
|
+
"\nError: the input file should be in a valid Fortify FPR format (broken unified node pool)."
|
|
7379
|
+
);
|
|
7380
|
+
}
|
|
7381
|
+
} else {
|
|
7382
|
+
sourceLocation = node;
|
|
7383
|
+
}
|
|
7384
|
+
return {
|
|
7385
|
+
physicalLocation: {
|
|
7386
|
+
artifactLocation: {
|
|
7387
|
+
uri: sourceLocation.path
|
|
7388
|
+
},
|
|
7389
|
+
region: {
|
|
7390
|
+
startLine: parseInt(sourceLocation.line || "0", 10),
|
|
7391
|
+
endLine: sourceLocation.lineEnd ? parseInt(sourceLocation.lineEnd, 10) : void 0,
|
|
7392
|
+
startColumn: parseInt(sourceLocation.colStart || "0", 10),
|
|
7393
|
+
endColumn: sourceLocation.colEnd ? parseInt(sourceLocation.colEnd, 10) : void 0
|
|
7394
|
+
}
|
|
7395
|
+
}
|
|
7396
|
+
};
|
|
7397
|
+
});
|
|
7398
|
+
}
|
|
7399
|
+
|
|
7400
|
+
// src/args/options.ts
|
|
7401
|
+
import chalk2 from "chalk";
|
|
7402
|
+
|
|
7403
|
+
// src/constants.ts
|
|
7404
|
+
import path4 from "node:path";
|
|
7405
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
7406
|
+
import chalk from "chalk";
|
|
7407
|
+
import Debug4 from "debug";
|
|
7408
|
+
import * as dotenv from "dotenv";
|
|
7409
|
+
import { z as z24 } from "zod";
|
|
7410
|
+
var debug4 = Debug4("mobbdev:constants");
|
|
7411
|
+
var __dirname = path4.dirname(fileURLToPath2(import.meta.url));
|
|
7412
|
+
dotenv.config({ path: path4.join(__dirname, "../.env") });
|
|
7413
|
+
var scmFriendlyText = {
|
|
7414
|
+
["Ado" /* Ado */]: "Azure DevOps",
|
|
7415
|
+
["Bitbucket" /* Bitbucket */]: "Bitbucket",
|
|
7416
|
+
["GitHub" /* GitHub */]: "GitGub",
|
|
7417
|
+
["GitLab" /* GitLab */]: "GitLab"
|
|
7418
|
+
};
|
|
7419
|
+
var SCANNERS = {
|
|
7420
|
+
Checkmarx: "checkmarx",
|
|
7421
|
+
Codeql: "codeql",
|
|
7422
|
+
Fortify: "fortify",
|
|
7423
|
+
Snyk: "snyk",
|
|
7424
|
+
Sonarqube: "sonarqube",
|
|
7425
|
+
Semgrep: "semgrep",
|
|
7426
|
+
Datadog: "datadog"
|
|
7427
|
+
};
|
|
7428
|
+
var scannerToVulnerability_Report_Vendor_Enum = {
|
|
7429
|
+
[SCANNERS.Checkmarx]: "checkmarx" /* Checkmarx */,
|
|
7430
|
+
[SCANNERS.Snyk]: "snyk" /* Snyk */,
|
|
7431
|
+
[SCANNERS.Sonarqube]: "sonarqube" /* Sonarqube */,
|
|
7432
|
+
[SCANNERS.Codeql]: "codeql" /* Codeql */,
|
|
7433
|
+
[SCANNERS.Fortify]: "fortify" /* Fortify */,
|
|
7434
|
+
[SCANNERS.Semgrep]: "semgrep" /* Semgrep */,
|
|
7435
|
+
[SCANNERS.Datadog]: "datadog" /* Datadog */
|
|
7436
|
+
};
|
|
7437
|
+
var SupportedScannersZ = z24.enum([SCANNERS.Checkmarx, SCANNERS.Snyk]);
|
|
7438
|
+
var envVariablesSchema = z24.object({
|
|
7439
|
+
WEB_APP_URL: z24.string(),
|
|
7440
|
+
API_URL: z24.string(),
|
|
7441
|
+
HASURA_ACCESS_KEY: z24.string(),
|
|
7442
|
+
LOCAL_GRAPHQL_ENDPOINT: z24.string(),
|
|
7443
|
+
HTTP_PROXY: z24.string().optional().default(""),
|
|
7444
|
+
HTTPS_PROXY: z24.string().optional().default("")
|
|
7445
|
+
}).required();
|
|
7446
|
+
var envVariables = envVariablesSchema.parse(process.env);
|
|
7447
|
+
debug4("config %o", envVariables);
|
|
7448
|
+
var mobbAscii = `
|
|
7449
|
+
..
|
|
7450
|
+
..........
|
|
7451
|
+
.................
|
|
7452
|
+
...........................
|
|
7453
|
+
..............................
|
|
7454
|
+
................................
|
|
7455
|
+
..................................
|
|
7456
|
+
....................................
|
|
7457
|
+
.....................................
|
|
7458
|
+
.............................................
|
|
7459
|
+
.................................................
|
|
7460
|
+
............................... .................
|
|
7461
|
+
.................................. ............
|
|
7462
|
+
.................. ............. ..........
|
|
7463
|
+
......... ........ ......... ......
|
|
7464
|
+
............... ....
|
|
7465
|
+
.... ..
|
|
7466
|
+
|
|
7467
|
+
. ...
|
|
7468
|
+
..............
|
|
7469
|
+
......................
|
|
7470
|
+
...........................
|
|
7471
|
+
................................
|
|
7472
|
+
......................................
|
|
7473
|
+
...............................
|
|
7474
|
+
.................
|
|
7475
|
+
`;
|
|
7476
|
+
var PROJECT_DEFAULT_NAME = "My first project";
|
|
7477
|
+
var WEB_APP_URL = envVariables.WEB_APP_URL;
|
|
7478
|
+
var API_URL = envVariables.API_URL;
|
|
7479
|
+
var HASURA_ACCESS_KEY = envVariables.HASURA_ACCESS_KEY;
|
|
7480
|
+
var LOCAL_GRAPHQL_ENDPOINT = envVariables.LOCAL_GRAPHQL_ENDPOINT;
|
|
7481
|
+
var HTTPS_PROXY = envVariables.HTTPS_PROXY;
|
|
7482
|
+
var HTTP_PROXY = envVariables.HTTP_PROXY;
|
|
7483
|
+
var errorMessages = {
|
|
7484
|
+
missingCxProjectName: `project name ${chalk.bold(
|
|
7485
|
+
"(--cx-project-name)"
|
|
7486
|
+
)} is needed if you're using checkmarx`,
|
|
7487
|
+
missingUrl: `url ${chalk.bold(
|
|
7488
|
+
"(--url)"
|
|
7489
|
+
)} is needed if you're adding an SCM token`,
|
|
7490
|
+
invalidScmType: `SCM type ${chalk.bold(
|
|
7491
|
+
"(--scm-type)"
|
|
7492
|
+
)} is invalid, please use one of: ${Object.values(ScmType).join(", ")}`,
|
|
7493
|
+
missingToken: `SCM token ${chalk.bold(
|
|
7494
|
+
"(--token)"
|
|
7495
|
+
)} is needed if you're adding an SCM token`
|
|
7496
|
+
};
|
|
7497
|
+
var progressMassages = {
|
|
7498
|
+
processingVulnerabilityReportSuccess: "\u2699\uFE0F Vulnerability report proccessed successfully",
|
|
7499
|
+
processingVulnerabilityReport: "\u2699\uFE0F Proccessing vulnerability report",
|
|
7500
|
+
processingVulnerabilityReportFailed: "\u2699\uFE0F Error Proccessing vulnerability report"
|
|
7501
|
+
};
|
|
7502
|
+
var VUL_REPORT_DIGEST_TIMEOUT_MS = 1e3 * 60 * 30;
|
|
7503
|
+
|
|
7504
|
+
// src/args/options.ts
|
|
7505
|
+
var repoOption = {
|
|
7506
|
+
alias: "r",
|
|
7507
|
+
demandOption: true,
|
|
7508
|
+
type: "string",
|
|
7509
|
+
describe: chalk2.bold("Github / GitLab / Azure DevOps repository URL")
|
|
7510
|
+
};
|
|
7511
|
+
var projectNameOption = {
|
|
7512
|
+
type: "string",
|
|
7513
|
+
describe: chalk2.bold("Checkmarx project name (when scanning with Checkmarx)")
|
|
7514
|
+
};
|
|
7515
|
+
var yesOption = {
|
|
7516
|
+
alias: "yes",
|
|
7517
|
+
type: "boolean",
|
|
7518
|
+
describe: chalk2.bold("Skip prompts and use default values")
|
|
7519
|
+
};
|
|
7520
|
+
var refOption = {
|
|
7521
|
+
describe: chalk2.bold("Reference of the repository (branch, tag, commit)"),
|
|
7522
|
+
type: "string",
|
|
7523
|
+
demandOption: false
|
|
7524
|
+
};
|
|
7525
|
+
var organizationIdOptions = {
|
|
7526
|
+
describe: chalk2.bold("Organization id"),
|
|
7527
|
+
alias: "organization-id",
|
|
7528
|
+
type: "string",
|
|
7529
|
+
demandOption: false
|
|
7530
|
+
};
|
|
7531
|
+
var scannerOptions = {
|
|
7532
|
+
alias: "s",
|
|
7533
|
+
choices: Object.values(SCANNERS),
|
|
7534
|
+
describe: chalk2.bold("Select the scanner to use")
|
|
7535
|
+
};
|
|
7536
|
+
var mobbProjectNameOption = {
|
|
7537
|
+
type: "string",
|
|
7538
|
+
describe: chalk2.bold("Mobb project name"),
|
|
7539
|
+
default: PROJECT_DEFAULT_NAME
|
|
7540
|
+
};
|
|
7541
|
+
var ciOption = {
|
|
7542
|
+
describe: chalk2.bold(
|
|
7543
|
+
"Run in CI mode, prompts and browser will not be opened"
|
|
7544
|
+
),
|
|
7545
|
+
type: "boolean",
|
|
7546
|
+
default: false
|
|
7547
|
+
};
|
|
7548
|
+
var apiKeyOption = {
|
|
7549
|
+
type: "string",
|
|
7550
|
+
describe: chalk2.bold("Mobb authentication api-key")
|
|
7551
|
+
};
|
|
7552
|
+
var commitHashOption = {
|
|
7553
|
+
alias: "ch",
|
|
7554
|
+
describe: chalk2.bold("Hash of the commit"),
|
|
7555
|
+
type: "string"
|
|
7556
|
+
};
|
|
7557
|
+
var autoPrOption = {
|
|
7558
|
+
describe: chalk2.bold("Enable automatic pull requests for new fixes"),
|
|
7559
|
+
type: "boolean",
|
|
7560
|
+
default: false
|
|
7561
|
+
};
|
|
7562
|
+
var commitDirectlyOption = {
|
|
7563
|
+
describe: chalk2.bold(
|
|
7564
|
+
"Commit directly to the scanned branch instead of creating a pull request"
|
|
7565
|
+
),
|
|
7566
|
+
type: "boolean",
|
|
7567
|
+
default: false
|
|
7568
|
+
};
|
|
7569
|
+
var scmTypeOption = {
|
|
7570
|
+
demandOption: true,
|
|
7571
|
+
describe: chalk2.bold("SCM type"),
|
|
7572
|
+
choices: Object.values(ScmType)
|
|
7573
|
+
};
|
|
7574
|
+
var urlOption = {
|
|
7575
|
+
describe: chalk2.bold(
|
|
7576
|
+
`URL of the repository (used in ${Object.values(ScmType).join(", ")})`
|
|
7577
|
+
),
|
|
7578
|
+
type: "string",
|
|
7579
|
+
demandOption: true
|
|
7580
|
+
};
|
|
7581
|
+
var scmOrgOption = {
|
|
7582
|
+
describe: chalk2.bold("Organization name in SCM (used in Azure DevOps)"),
|
|
7583
|
+
type: "string"
|
|
7584
|
+
};
|
|
7585
|
+
var scmRefreshTokenOption = {
|
|
7586
|
+
describe: chalk2.bold("SCM refresh token (used in GitLab)"),
|
|
7587
|
+
type: "string"
|
|
7588
|
+
};
|
|
7589
|
+
var scmTokenOption = {
|
|
7590
|
+
describe: chalk2.bold("SCM API token"),
|
|
7591
|
+
type: "string",
|
|
7592
|
+
demandOption: true
|
|
7593
|
+
};
|
|
7594
|
+
var convertToSarifInputFilePathOption = {
|
|
7595
|
+
demandOption: true,
|
|
7596
|
+
describe: chalk2.bold("Original SAST report file path"),
|
|
7597
|
+
type: "string"
|
|
7598
|
+
};
|
|
7599
|
+
var convertToSarifOutputFilePathOption = {
|
|
7600
|
+
demandOption: true,
|
|
7601
|
+
describe: chalk2.bold("Output SARIF report file path"),
|
|
7602
|
+
type: "string"
|
|
7603
|
+
};
|
|
7604
|
+
var convertToSarifInputFileFormatOption = {
|
|
7605
|
+
demandOption: true,
|
|
7606
|
+
choices: Object.values(ConvertToSarifInputFileFormat),
|
|
7607
|
+
describe: chalk2.bold("SAST report file type")
|
|
6989
7608
|
};
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
super(url, accessToken, scmOrg);
|
|
6998
|
-
}
|
|
6999
|
-
async getUrlWithCredentials() {
|
|
7000
|
-
console.warn("getUrlWithCredentials() returning empty string");
|
|
7001
|
-
return "";
|
|
7002
|
-
}
|
|
7003
|
-
async createSubmitRequest(_params) {
|
|
7004
|
-
console.warn("createSubmitRequest() returning empty string");
|
|
7005
|
-
return "";
|
|
7006
|
-
}
|
|
7007
|
-
get scmLibType() {
|
|
7008
|
-
console.warn("scmLibType returning GITHUB as default");
|
|
7009
|
-
return "GITHUB" /* GITHUB */;
|
|
7010
|
-
}
|
|
7011
|
-
getAuthHeaders() {
|
|
7012
|
-
console.warn("getAuthHeaders() returning empty object");
|
|
7013
|
-
return {};
|
|
7014
|
-
}
|
|
7015
|
-
async getDownloadUrl(_sha) {
|
|
7016
|
-
console.warn("getDownloadUrl() returning empty string");
|
|
7017
|
-
return "";
|
|
7018
|
-
}
|
|
7019
|
-
async getIsRemoteBranch(_branch) {
|
|
7020
|
-
console.warn("getIsRemoteBranch() returning false");
|
|
7021
|
-
return false;
|
|
7022
|
-
}
|
|
7023
|
-
async validateParams() {
|
|
7024
|
-
console.warn("validateParams() no-op");
|
|
7025
|
-
}
|
|
7026
|
-
async getRepoList(_scmOrg) {
|
|
7027
|
-
console.warn("getRepoList() returning empty array");
|
|
7028
|
-
return [];
|
|
7029
|
-
}
|
|
7030
|
-
async getBranchList() {
|
|
7031
|
-
console.warn("getBranchList() returning empty array");
|
|
7032
|
-
return [];
|
|
7033
|
-
}
|
|
7034
|
-
async getUsername() {
|
|
7035
|
-
console.warn("getUsername() returning empty string");
|
|
7036
|
-
return "";
|
|
7037
|
-
}
|
|
7038
|
-
async getSubmitRequestStatus(_scmSubmitRequestId) {
|
|
7039
|
-
console.warn("getSubmitRequestStatus() returning ERROR");
|
|
7040
|
-
return "error";
|
|
7041
|
-
}
|
|
7042
|
-
async getUserHasAccessToRepo() {
|
|
7043
|
-
console.warn("getUserHasAccessToRepo() returning false");
|
|
7044
|
-
return false;
|
|
7045
|
-
}
|
|
7046
|
-
async getRepoBlameRanges(_ref, _path) {
|
|
7047
|
-
console.warn("getRepoBlameRanges() returning empty array");
|
|
7048
|
-
return [];
|
|
7049
|
-
}
|
|
7050
|
-
async getReferenceData(_ref) {
|
|
7051
|
-
console.warn("getReferenceData() returning null/empty defaults");
|
|
7052
|
-
return {
|
|
7053
|
-
type: "BRANCH" /* BRANCH */,
|
|
7054
|
-
sha: "",
|
|
7055
|
-
date: void 0
|
|
7056
|
-
};
|
|
7057
|
-
}
|
|
7058
|
-
async getRepoDefaultBranch() {
|
|
7059
|
-
console.warn("getRepoDefaultBranch() returning empty string");
|
|
7060
|
-
return "";
|
|
7061
|
-
}
|
|
7062
|
-
async getSubmitRequestUrl(_submitRequestIdNumber) {
|
|
7063
|
-
console.warn("getSubmitRequestUrl() returning empty string");
|
|
7064
|
-
return "";
|
|
7065
|
-
}
|
|
7066
|
-
async getSubmitRequestId(_submitRequestUrl) {
|
|
7067
|
-
console.warn("getSubmitRequestId() returning empty string");
|
|
7068
|
-
return "";
|
|
7069
|
-
}
|
|
7070
|
-
async getCommitUrl(_commitId) {
|
|
7071
|
-
console.warn("getCommitUrl() returning empty string");
|
|
7072
|
-
return "";
|
|
7073
|
-
}
|
|
7074
|
-
async _getUsernameForAuthUrl() {
|
|
7075
|
-
console.warn("_getUsernameForAuthUrl() returning empty string");
|
|
7076
|
-
return "";
|
|
7077
|
-
}
|
|
7078
|
-
async addCommentToSubmitRequest(_submitRequestId, _comment) {
|
|
7079
|
-
console.warn("addCommentToSubmitRequest() no-op");
|
|
7080
|
-
}
|
|
7609
|
+
var convertToSarifCodePathPatternsOption = {
|
|
7610
|
+
demandOption: false,
|
|
7611
|
+
describe: chalk2.bold(
|
|
7612
|
+
"Glob-like patterns. Any code node with this pattern makes the issue be included."
|
|
7613
|
+
),
|
|
7614
|
+
type: "string",
|
|
7615
|
+
array: true
|
|
7081
7616
|
};
|
|
7082
7617
|
|
|
7083
|
-
// src/
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
await scm.validateParams();
|
|
7096
|
-
return scm;
|
|
7097
|
-
}
|
|
7098
|
-
case "ADO" /* ADO */: {
|
|
7099
|
-
const scm = new AdoSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
7100
|
-
await scm.getAdoSdk();
|
|
7101
|
-
await scm.validateParams();
|
|
7102
|
-
return scm;
|
|
7103
|
-
}
|
|
7104
|
-
case "BITBUCKET" /* BITBUCKET */: {
|
|
7105
|
-
const scm = new BitbucketSCMLib(trimmedUrl, accessToken, scmOrg);
|
|
7106
|
-
await scm.validateParams();
|
|
7107
|
-
return scm;
|
|
7108
|
-
}
|
|
7109
|
-
}
|
|
7110
|
-
} catch (e) {
|
|
7111
|
-
if (e instanceof InvalidRepoUrlError && url) {
|
|
7112
|
-
throw new RepoNoTokenAccessError(
|
|
7113
|
-
"no access to repo",
|
|
7114
|
-
scmLibScmTypeToScmType[z24.nativeEnum(ScmLibScmType).parse(scmType)]
|
|
7115
|
-
);
|
|
7116
|
-
}
|
|
7117
|
-
console.error(`error validating scm: ${scmType} `, e);
|
|
7118
|
-
if (propagateExceptions) {
|
|
7119
|
-
throw e;
|
|
7120
|
-
}
|
|
7618
|
+
// src/args/commands/convert_to_sarif.ts
|
|
7619
|
+
function convertToSarifBuilder(args) {
|
|
7620
|
+
return args.option("input-file-path", convertToSarifInputFilePathOption).option("input-file-format", convertToSarifInputFileFormatOption).option("output-file-path", convertToSarifOutputFilePathOption).option("code-path-patterns", convertToSarifCodePathPatternsOption).example(
|
|
7621
|
+
"npx mobbdev@latest convert-to-sarif --input-file-path /path/to/vuln-report.fpr --input-file-format FortifyFPR --output-file-path /path/to/vuln-report.sarif --code-path-patterns **/*.ts --code-path-patterns **/*.js",
|
|
7622
|
+
"convert an existing SAST report to SARIF format"
|
|
7623
|
+
).help().demandOption(["input-file-path", "input-file-format", "output-file-path"]);
|
|
7624
|
+
}
|
|
7625
|
+
async function validateConvertToSarifOptions(args) {
|
|
7626
|
+
if (!fs4.existsSync(args.inputFilePath)) {
|
|
7627
|
+
throw new CliError(
|
|
7628
|
+
"\nError: --input-file-path flag should point to an existing file"
|
|
7629
|
+
);
|
|
7121
7630
|
}
|
|
7122
|
-
return new StubSCMLib(trimmedUrl, void 0, void 0);
|
|
7123
7631
|
}
|
|
7632
|
+
async function convertToSarifHandler(args) {
|
|
7633
|
+
await validateConvertToSarifOptions(args);
|
|
7634
|
+
await convertToSarif(args);
|
|
7635
|
+
}
|
|
7636
|
+
|
|
7637
|
+
// src/types.ts
|
|
7638
|
+
var mobbCliCommand = {
|
|
7639
|
+
addScmToken: "add-scm-token",
|
|
7640
|
+
scan: "scan",
|
|
7641
|
+
analyze: "analyze",
|
|
7642
|
+
review: "review",
|
|
7643
|
+
convertToSarif: "convert-to-sarif"
|
|
7644
|
+
};
|
|
7645
|
+
|
|
7646
|
+
// src/args/yargs.ts
|
|
7647
|
+
import chalk10 from "chalk";
|
|
7648
|
+
import yargs from "yargs/yargs";
|
|
7649
|
+
|
|
7650
|
+
// src/args/commands/analyze.ts
|
|
7651
|
+
import fs7 from "node:fs";
|
|
7652
|
+
|
|
7653
|
+
// src/commands/index.ts
|
|
7654
|
+
import crypto from "node:crypto";
|
|
7655
|
+
import os from "node:os";
|
|
7656
|
+
|
|
7657
|
+
// src/features/analysis/index.ts
|
|
7658
|
+
import fs6 from "node:fs";
|
|
7659
|
+
import fsPromises from "node:fs/promises";
|
|
7660
|
+
import path7 from "node:path";
|
|
7661
|
+
import { env as env2 } from "node:process";
|
|
7662
|
+
import { pipeline } from "node:stream/promises";
|
|
7663
|
+
import chalk5 from "chalk";
|
|
7664
|
+
import Configstore from "configstore";
|
|
7665
|
+
import Debug18 from "debug";
|
|
7666
|
+
import extract from "extract-zip";
|
|
7667
|
+
import { createSpinner as createSpinner4 } from "nanospinner";
|
|
7668
|
+
import fetch4 from "node-fetch";
|
|
7669
|
+
import open2 from "open";
|
|
7670
|
+
import tmp2 from "tmp";
|
|
7671
|
+
import { z as z29 } from "zod";
|
|
7672
|
+
|
|
7673
|
+
// src/features/analysis/add_fix_comments_for_pr/add_fix_comments_for_pr.ts
|
|
7674
|
+
import Debug8 from "debug";
|
|
7124
7675
|
|
|
7125
7676
|
// src/features/analysis/add_fix_comments_for_pr/utils/utils.ts
|
|
7126
7677
|
import Debug7 from "debug";
|
|
@@ -7390,7 +7941,7 @@ async function postIssueComment(params) {
|
|
|
7390
7941
|
fpDescription
|
|
7391
7942
|
} = params;
|
|
7392
7943
|
const {
|
|
7393
|
-
path:
|
|
7944
|
+
path: path9,
|
|
7394
7945
|
startLine,
|
|
7395
7946
|
vulnerabilityReportIssue: {
|
|
7396
7947
|
vulnerabilityReportIssueTags,
|
|
@@ -7405,7 +7956,7 @@ async function postIssueComment(params) {
|
|
|
7405
7956
|
Refresh the page in order to see the changes.`,
|
|
7406
7957
|
pull_number: pullRequest,
|
|
7407
7958
|
commit_id: commitSha,
|
|
7408
|
-
path:
|
|
7959
|
+
path: path9,
|
|
7409
7960
|
line: startLine
|
|
7410
7961
|
});
|
|
7411
7962
|
const commentId = commentRes.data.id;
|
|
@@ -7439,7 +7990,7 @@ async function postFixComment(params) {
|
|
|
7439
7990
|
scanner
|
|
7440
7991
|
} = params;
|
|
7441
7992
|
const {
|
|
7442
|
-
path:
|
|
7993
|
+
path: path9,
|
|
7443
7994
|
startLine,
|
|
7444
7995
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
7445
7996
|
vulnerabilityReportIssueId
|
|
@@ -7457,7 +8008,7 @@ async function postFixComment(params) {
|
|
|
7457
8008
|
Refresh the page in order to see the changes.`,
|
|
7458
8009
|
pull_number: pullRequest,
|
|
7459
8010
|
commit_id: commitSha,
|
|
7460
|
-
path:
|
|
8011
|
+
path: path9,
|
|
7461
8012
|
line: startLine
|
|
7462
8013
|
});
|
|
7463
8014
|
const commentId = commentRes.data.id;
|
|
@@ -8275,9 +8826,9 @@ var GQLClient = class {
|
|
|
8275
8826
|
};
|
|
8276
8827
|
|
|
8277
8828
|
// src/features/analysis/pack.ts
|
|
8278
|
-
import
|
|
8279
|
-
import
|
|
8280
|
-
import
|
|
8829
|
+
import fs5 from "node:fs";
|
|
8830
|
+
import path5 from "node:path";
|
|
8831
|
+
import AdmZip2 from "adm-zip";
|
|
8281
8832
|
import Debug13 from "debug";
|
|
8282
8833
|
import { globby } from "globby";
|
|
8283
8834
|
import { isBinary } from "istextorbinary";
|
|
@@ -8339,23 +8890,23 @@ async function pack(srcDirPath, vulnFiles) {
|
|
|
8339
8890
|
dot: true
|
|
8340
8891
|
});
|
|
8341
8892
|
debug13("files found %d", filepaths.length);
|
|
8342
|
-
const zip = new
|
|
8893
|
+
const zip = new AdmZip2();
|
|
8343
8894
|
debug13("compressing files");
|
|
8344
8895
|
for (const filepath of filepaths) {
|
|
8345
|
-
const absFilepath =
|
|
8896
|
+
const absFilepath = path5.join(srcDirPath, filepath.toString());
|
|
8346
8897
|
vulnFiles = vulnFiles.concat(_get_manifest_files_suffixes());
|
|
8347
8898
|
if (!endsWithAny(
|
|
8348
|
-
absFilepath.toString().replaceAll(
|
|
8899
|
+
absFilepath.toString().replaceAll(path5.win32.sep, path5.posix.sep),
|
|
8349
8900
|
vulnFiles
|
|
8350
8901
|
)) {
|
|
8351
8902
|
debug13("ignoring %s because it is not a vulnerability file", filepath);
|
|
8352
8903
|
continue;
|
|
8353
8904
|
}
|
|
8354
|
-
if (
|
|
8905
|
+
if (fs5.lstatSync(absFilepath).size > MAX_FILE_SIZE) {
|
|
8355
8906
|
debug13("ignoring %s because the size is > 5MB", filepath);
|
|
8356
8907
|
continue;
|
|
8357
8908
|
}
|
|
8358
|
-
const data = git ? await git.showBuffer([`HEAD:./${filepath}`]) :
|
|
8909
|
+
const data = git ? await git.showBuffer([`HEAD:./${filepath}`]) : fs5.readFileSync(absFilepath);
|
|
8359
8910
|
if (isBinary(null, data)) {
|
|
8360
8911
|
debug13("ignoring %s because is seems to be a binary file", filepath);
|
|
8361
8912
|
continue;
|
|
@@ -8367,8 +8918,8 @@ async function pack(srcDirPath, vulnFiles) {
|
|
|
8367
8918
|
}
|
|
8368
8919
|
async function repackFpr(fprPath) {
|
|
8369
8920
|
debug13("repack fpr file %s", fprPath);
|
|
8370
|
-
const zipIn = new
|
|
8371
|
-
const zipOut = new
|
|
8921
|
+
const zipIn = new AdmZip2(fprPath);
|
|
8922
|
+
const zipOut = new AdmZip2();
|
|
8372
8923
|
const mappingXML = zipIn.readAsText("src-archive/index.xml", "utf-8");
|
|
8373
8924
|
const filesMapping = FPR_SOURCE_CODE_FILE_MAPPING_SCHEMA.parse(
|
|
8374
8925
|
await parseStringPromise(mappingXML)
|
|
@@ -8507,12 +9058,12 @@ function createChildProcess({ childProcess, name }, options) {
|
|
|
8507
9058
|
}
|
|
8508
9059
|
|
|
8509
9060
|
// src/features/analysis/scanners/checkmarx.ts
|
|
8510
|
-
import
|
|
9061
|
+
import chalk3 from "chalk";
|
|
8511
9062
|
import Debug15 from "debug";
|
|
8512
9063
|
import { existsSync } from "fs";
|
|
8513
9064
|
import { createSpinner as createSpinner2 } from "nanospinner";
|
|
8514
9065
|
import { type } from "os";
|
|
8515
|
-
import
|
|
9066
|
+
import path6 from "path";
|
|
8516
9067
|
var debug14 = Debug15("mobbdev:checkmarx");
|
|
8517
9068
|
var require2 = createRequire(import.meta.url);
|
|
8518
9069
|
var getCheckmarxPath = () => {
|
|
@@ -8572,9 +9123,9 @@ async function getCheckmarxReport({ reportPath, repositoryRoot, branch, projectN
|
|
|
8572
9123
|
await startCheckmarxConfigationPrompt();
|
|
8573
9124
|
await validateCheckamxCredentials();
|
|
8574
9125
|
}
|
|
8575
|
-
const extension =
|
|
8576
|
-
const filePath =
|
|
8577
|
-
const fileName =
|
|
9126
|
+
const extension = path6.extname(reportPath);
|
|
9127
|
+
const filePath = path6.dirname(reportPath);
|
|
9128
|
+
const fileName = path6.basename(reportPath, extension);
|
|
8578
9129
|
const checkmarxCommandArgs = getCheckmarxCommandArgs({
|
|
8579
9130
|
repoPath: repositoryRoot,
|
|
8580
9131
|
branch,
|
|
@@ -8600,7 +9151,7 @@ async function throwCheckmarxConfigError() {
|
|
|
8600
9151
|
await createSpinner2("\u{1F513} Checkmarx is not configued correctly").start().error();
|
|
8601
9152
|
throw new CliError(
|
|
8602
9153
|
`Checkmarx is not configued correctly
|
|
8603
|
-
you can configure it by using the ${
|
|
9154
|
+
you can configure it by using the ${chalk3.bold(
|
|
8604
9155
|
"cx configure"
|
|
8605
9156
|
)} command`
|
|
8606
9157
|
);
|
|
@@ -8608,8 +9159,8 @@ async function throwCheckmarxConfigError() {
|
|
|
8608
9159
|
async function validateCheckamxCredentials() {
|
|
8609
9160
|
console.log(`
|
|
8610
9161
|
Here's a suggestion for checkmarx configuation:
|
|
8611
|
-
${
|
|
8612
|
-
${
|
|
9162
|
+
${chalk3.bold("AST Base URI:")} https://ast.checkmarx.net
|
|
9163
|
+
${chalk3.bold("AST Base Auth URI (IAM):")} https://iam.checkmarx.net
|
|
8613
9164
|
`);
|
|
8614
9165
|
await forkCheckmarx(CONFIGURE_COMMAND, { display: true });
|
|
8615
9166
|
const { code: loginCode } = await forkCheckmarx(VALIDATE_COMMAND, {
|
|
@@ -8628,7 +9179,7 @@ async function validateCheckamxCredentials() {
|
|
|
8628
9179
|
|
|
8629
9180
|
// src/features/analysis/scanners/snyk.ts
|
|
8630
9181
|
import { createRequire as createRequire2 } from "node:module";
|
|
8631
|
-
import
|
|
9182
|
+
import chalk4 from "chalk";
|
|
8632
9183
|
import Debug16 from "debug";
|
|
8633
9184
|
import { createSpinner as createSpinner3 } from "nanospinner";
|
|
8634
9185
|
import open from "open";
|
|
@@ -8675,7 +9226,7 @@ async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
|
8675
9226
|
await open(SNYK_ARTICLE_URL);
|
|
8676
9227
|
}
|
|
8677
9228
|
console.log(
|
|
8678
|
-
|
|
9229
|
+
chalk4.bgBlue(
|
|
8679
9230
|
"\nPlease enable Snyk Code in your Snyk account and try again."
|
|
8680
9231
|
)
|
|
8681
9232
|
);
|
|
@@ -8757,7 +9308,7 @@ async function downloadRepo({
|
|
|
8757
9308
|
const { createSpinner: createSpinner5 } = Spinner2({ ci });
|
|
8758
9309
|
const repoSpinner = createSpinner5("\u{1F4BE} Downloading Repo").start();
|
|
8759
9310
|
debug17("download repo %s %s %s", repoUrl, dirname);
|
|
8760
|
-
const zipFilePath =
|
|
9311
|
+
const zipFilePath = path7.join(dirname, "repo.zip");
|
|
8761
9312
|
debug17("download URL: %s auth headers: %o", downloadUrl, authHeaders);
|
|
8762
9313
|
const response = await fetch4(downloadUrl, {
|
|
8763
9314
|
method: "GET",
|
|
@@ -8768,21 +9319,21 @@ async function downloadRepo({
|
|
|
8768
9319
|
if (!response.ok) {
|
|
8769
9320
|
debug17("SCM zipball request failed %s %s", response.body, response.status);
|
|
8770
9321
|
repoSpinner.error({ text: "\u{1F4BE} Repo download failed" });
|
|
8771
|
-
throw new Error(`Can't access ${
|
|
9322
|
+
throw new Error(`Can't access ${chalk5.bold(repoUrl)}`);
|
|
8772
9323
|
}
|
|
8773
|
-
const fileWriterStream =
|
|
9324
|
+
const fileWriterStream = fs6.createWriteStream(zipFilePath);
|
|
8774
9325
|
if (!response.body) {
|
|
8775
9326
|
throw new Error("Response body is empty");
|
|
8776
9327
|
}
|
|
8777
9328
|
await pipeline(response.body, fileWriterStream);
|
|
8778
9329
|
await extract(zipFilePath, { dir: dirname });
|
|
8779
|
-
const repoRoot =
|
|
9330
|
+
const repoRoot = fs6.readdirSync(dirname, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name)[0];
|
|
8780
9331
|
if (!repoRoot) {
|
|
8781
9332
|
throw new Error("Repo root not found");
|
|
8782
9333
|
}
|
|
8783
9334
|
debug17("repo root %s", repoRoot);
|
|
8784
9335
|
repoSpinner.success({ text: "\u{1F4BE} Repo downloaded successfully" });
|
|
8785
|
-
return
|
|
9336
|
+
return path7.join(dirname, repoRoot);
|
|
8786
9337
|
}
|
|
8787
9338
|
var getReportUrl = ({
|
|
8788
9339
|
organizationId,
|
|
@@ -8793,7 +9344,7 @@ var debug17 = Debug18("mobbdev:index");
|
|
|
8793
9344
|
var config2 = new Configstore(packageJson.name, { apiToken: "" });
|
|
8794
9345
|
debug17("config %o", config2);
|
|
8795
9346
|
async function runAnalysis(params, options) {
|
|
8796
|
-
const tmpObj =
|
|
9347
|
+
const tmpObj = tmp2.dirSync({
|
|
8797
9348
|
unsafeCleanup: true
|
|
8798
9349
|
});
|
|
8799
9350
|
try {
|
|
@@ -8892,7 +9443,7 @@ async function getReport(params, { skipPrompts }) {
|
|
|
8892
9443
|
authHeaders: scm.getAuthHeaders(),
|
|
8893
9444
|
downloadUrl
|
|
8894
9445
|
});
|
|
8895
|
-
const reportPath =
|
|
9446
|
+
const reportPath = path7.join(dirname, "report.json");
|
|
8896
9447
|
switch (scanner) {
|
|
8897
9448
|
case "snyk":
|
|
8898
9449
|
await getSnykReport(reportPath, repositoryRoot, { skipPrompts });
|
|
@@ -9099,11 +9650,11 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
9099
9650
|
fixReportId: reportUploadInfo.fixReportId
|
|
9100
9651
|
});
|
|
9101
9652
|
!ci && console.log("You can access the analysis at: \n");
|
|
9102
|
-
console.log(
|
|
9653
|
+
console.log(chalk5.bold(reportUrl));
|
|
9103
9654
|
!skipPrompts && await mobbAnalysisPrompt();
|
|
9104
9655
|
!ci && open2(reportUrl);
|
|
9105
9656
|
!ci && console.log(
|
|
9106
|
-
|
|
9657
|
+
chalk5.bgBlue("\n\n My work here is done for now, see you soon! \u{1F575}\uFE0F\u200D\u2642\uFE0F ")
|
|
9107
9658
|
);
|
|
9108
9659
|
}
|
|
9109
9660
|
async function handleScmIntegration(oldToken, scmAuthUrl2, repoUrl) {
|
|
@@ -9180,7 +9731,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
9180
9731
|
const zippingSpinner = createSpinner5("\u{1F4E6} Zipping repo").start();
|
|
9181
9732
|
let zipBuffer;
|
|
9182
9733
|
let gitInfo = { success: false };
|
|
9183
|
-
if (srcFileStatus.isFile() &&
|
|
9734
|
+
if (srcFileStatus.isFile() && path7.extname(srcPath).toLowerCase() === ".fpr") {
|
|
9184
9735
|
zipBuffer = await repackFpr(srcPath);
|
|
9185
9736
|
} else {
|
|
9186
9737
|
gitInfo = await getGitInfo(srcPath);
|
|
@@ -9333,7 +9884,7 @@ async function waitForAnaysisAndReviewPr({
|
|
|
9333
9884
|
}
|
|
9334
9885
|
|
|
9335
9886
|
// src/commands/index.ts
|
|
9336
|
-
import
|
|
9887
|
+
import chalk6 from "chalk";
|
|
9337
9888
|
import chalkAnimation from "chalk-animation";
|
|
9338
9889
|
import Configstore2 from "configstore";
|
|
9339
9890
|
import Debug19 from "debug";
|
|
@@ -9466,7 +10017,7 @@ async function showWelcomeMessage(skipPrompts = false) {
|
|
|
9466
10017
|
var LOGIN_MAX_WAIT = 10 * 60 * 1e3;
|
|
9467
10018
|
var LOGIN_CHECK_DELAY = 5 * 1e3;
|
|
9468
10019
|
var webLoginUrl = `${WEB_APP_URL}/cli-login`;
|
|
9469
|
-
var MOBB_LOGIN_REQUIRED_MSG = `\u{1F513} Login to Mobb is Required, you will be redirected to our login page, once the authorization is complete return to this prompt, ${
|
|
10020
|
+
var MOBB_LOGIN_REQUIRED_MSG = `\u{1F513} Login to Mobb is Required, you will be redirected to our login page, once the authorization is complete return to this prompt, ${chalk6.bgBlue(
|
|
9470
10021
|
"press any key to continue"
|
|
9471
10022
|
)};`;
|
|
9472
10023
|
async function handleMobbLogin({
|
|
@@ -9560,101 +10111,9 @@ async function handleMobbLogin({
|
|
|
9560
10111
|
// src/args/commands/analyze.ts
|
|
9561
10112
|
import chalk8 from "chalk";
|
|
9562
10113
|
|
|
9563
|
-
// src/args/options.ts
|
|
9564
|
-
import chalk6 from "chalk";
|
|
9565
|
-
var repoOption = {
|
|
9566
|
-
alias: "r",
|
|
9567
|
-
demandOption: true,
|
|
9568
|
-
type: "string",
|
|
9569
|
-
describe: chalk6.bold("Github / GitLab / Azure DevOps repository URL")
|
|
9570
|
-
};
|
|
9571
|
-
var projectNameOption = {
|
|
9572
|
-
type: "string",
|
|
9573
|
-
describe: chalk6.bold("Checkmarx project name (when scanning with Checkmarx)")
|
|
9574
|
-
};
|
|
9575
|
-
var yesOption = {
|
|
9576
|
-
alias: "yes",
|
|
9577
|
-
type: "boolean",
|
|
9578
|
-
describe: chalk6.bold("Skip prompts and use default values")
|
|
9579
|
-
};
|
|
9580
|
-
var refOption = {
|
|
9581
|
-
describe: chalk6.bold("Reference of the repository (branch, tag, commit)"),
|
|
9582
|
-
type: "string",
|
|
9583
|
-
demandOption: false
|
|
9584
|
-
};
|
|
9585
|
-
var organizationIdOptions = {
|
|
9586
|
-
describe: chalk6.bold("Organization id"),
|
|
9587
|
-
alias: "organization-id",
|
|
9588
|
-
type: "string",
|
|
9589
|
-
demandOption: false
|
|
9590
|
-
};
|
|
9591
|
-
var scannerOptions = {
|
|
9592
|
-
alias: "s",
|
|
9593
|
-
choices: Object.values(SCANNERS),
|
|
9594
|
-
describe: chalk6.bold("Select the scanner to use")
|
|
9595
|
-
};
|
|
9596
|
-
var mobbProjectNameOption = {
|
|
9597
|
-
type: "string",
|
|
9598
|
-
describe: chalk6.bold("Mobb project name"),
|
|
9599
|
-
default: PROJECT_DEFAULT_NAME
|
|
9600
|
-
};
|
|
9601
|
-
var ciOption = {
|
|
9602
|
-
describe: chalk6.bold(
|
|
9603
|
-
"Run in CI mode, prompts and browser will not be opened"
|
|
9604
|
-
),
|
|
9605
|
-
type: "boolean",
|
|
9606
|
-
default: false
|
|
9607
|
-
};
|
|
9608
|
-
var apiKeyOption = {
|
|
9609
|
-
type: "string",
|
|
9610
|
-
describe: chalk6.bold("Mobb authentication api-key")
|
|
9611
|
-
};
|
|
9612
|
-
var commitHashOption = {
|
|
9613
|
-
alias: "ch",
|
|
9614
|
-
describe: chalk6.bold("Hash of the commit"),
|
|
9615
|
-
type: "string"
|
|
9616
|
-
};
|
|
9617
|
-
var autoPrOption = {
|
|
9618
|
-
describe: chalk6.bold("Enable automatic pull requests for new fixes"),
|
|
9619
|
-
type: "boolean",
|
|
9620
|
-
default: false
|
|
9621
|
-
};
|
|
9622
|
-
var commitDirectlyOption = {
|
|
9623
|
-
describe: chalk6.bold(
|
|
9624
|
-
"Commit directly to the scanned branch instead of creating a pull request"
|
|
9625
|
-
),
|
|
9626
|
-
type: "boolean",
|
|
9627
|
-
default: false
|
|
9628
|
-
};
|
|
9629
|
-
var scmTypeOption = {
|
|
9630
|
-
demandOption: true,
|
|
9631
|
-
describe: chalk6.bold("SCM type"),
|
|
9632
|
-
choices: Object.values(ScmType)
|
|
9633
|
-
};
|
|
9634
|
-
var urlOption = {
|
|
9635
|
-
describe: chalk6.bold(
|
|
9636
|
-
`URL of the repository (used in ${Object.values(ScmType).join(", ")})`
|
|
9637
|
-
),
|
|
9638
|
-
type: "string",
|
|
9639
|
-
demandOption: true
|
|
9640
|
-
};
|
|
9641
|
-
var scmOrgOption = {
|
|
9642
|
-
describe: chalk6.bold("Organization name in SCM (used in Azure DevOps)"),
|
|
9643
|
-
type: "string"
|
|
9644
|
-
};
|
|
9645
|
-
var scmRefreshTokenOption = {
|
|
9646
|
-
describe: chalk6.bold("SCM refresh token (used in GitLab)"),
|
|
9647
|
-
type: "string"
|
|
9648
|
-
};
|
|
9649
|
-
var scmTokenOption = {
|
|
9650
|
-
describe: chalk6.bold("SCM API token"),
|
|
9651
|
-
type: "string",
|
|
9652
|
-
demandOption: true
|
|
9653
|
-
};
|
|
9654
|
-
|
|
9655
10114
|
// src/args/validation.ts
|
|
9656
10115
|
import chalk7 from "chalk";
|
|
9657
|
-
import
|
|
10116
|
+
import path8 from "path";
|
|
9658
10117
|
import { z as z30 } from "zod";
|
|
9659
10118
|
function throwRepoUrlErrorMessage({
|
|
9660
10119
|
error,
|
|
@@ -9698,7 +10157,7 @@ function validateRepoUrl(args) {
|
|
|
9698
10157
|
}
|
|
9699
10158
|
var supportExtensions = [".json", ".xml", ".fpr", ".sarif"];
|
|
9700
10159
|
function validateReportFileFormat(reportFile) {
|
|
9701
|
-
if (!supportExtensions.includes(
|
|
10160
|
+
if (!supportExtensions.includes(path8.extname(reportFile))) {
|
|
9702
10161
|
throw new CliError(
|
|
9703
10162
|
`
|
|
9704
10163
|
${chalk7.bold(
|
|
@@ -9741,7 +10200,7 @@ function analyzeBuilder(yargs2) {
|
|
|
9741
10200
|
).help();
|
|
9742
10201
|
}
|
|
9743
10202
|
function validateAnalyzeOptions(argv) {
|
|
9744
|
-
if (!
|
|
10203
|
+
if (!fs7.existsSync(argv.f)) {
|
|
9745
10204
|
throw new CliError(`
|
|
9746
10205
|
Can't access ${chalk8.bold(argv.f)}`);
|
|
9747
10206
|
}
|
|
@@ -9773,7 +10232,7 @@ async function analyzeHandler(args) {
|
|
|
9773
10232
|
}
|
|
9774
10233
|
|
|
9775
10234
|
// src/args/commands/review.ts
|
|
9776
|
-
import
|
|
10235
|
+
import fs8 from "node:fs";
|
|
9777
10236
|
import chalk9 from "chalk";
|
|
9778
10237
|
function reviewBuilder(yargs2) {
|
|
9779
10238
|
return yargs2.option("f", {
|
|
@@ -9810,7 +10269,7 @@ function reviewBuilder(yargs2) {
|
|
|
9810
10269
|
).help();
|
|
9811
10270
|
}
|
|
9812
10271
|
function validateReviewOptions(argv) {
|
|
9813
|
-
if (!
|
|
10272
|
+
if (!fs8.existsSync(argv.f)) {
|
|
9814
10273
|
throw new CliError(`
|
|
9815
10274
|
Can't access ${chalk9.bold(argv.f)}`);
|
|
9816
10275
|
}
|
|
@@ -9926,6 +10385,11 @@ var parseArgs = async (args) => {
|
|
|
9926
10385
|
),
|
|
9927
10386
|
addScmTokenBuilder,
|
|
9928
10387
|
addScmTokenHandler
|
|
10388
|
+
).command(
|
|
10389
|
+
mobbCliCommand.convertToSarif,
|
|
10390
|
+
chalk10.bold("Convert an existing SAST report to SARIF format."),
|
|
10391
|
+
convertToSarifBuilder,
|
|
10392
|
+
convertToSarifHandler
|
|
9929
10393
|
).example(
|
|
9930
10394
|
"npx mobbdev@latest scan -r https://github.com/WebGoat/WebGoat",
|
|
9931
10395
|
"Scan an existing repository"
|