@backstage-community/plugin-mend-backend 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/api/index.cjs.js +68 -0
- package/dist/api/index.cjs.js.map +1 -0
- package/dist/index.cjs.js +11 -741
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +9 -16
- package/dist/permission/conditions.cjs.js +33 -0
- package/dist/permission/conditions.cjs.js.map +1 -0
- package/dist/permission/permissions.cjs.js +17 -0
- package/dist/permission/permissions.cjs.js.map +1 -0
- package/dist/permission/rules.cjs.js +31 -0
- package/dist/permission/rules.cjs.js.map +1 -0
- package/dist/plugin.cjs.js +48 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/service/auth.service.cjs.js +93 -0
- package/dist/service/auth.service.cjs.js.map +1 -0
- package/dist/service/auth.service.helpers.cjs.js +14 -0
- package/dist/service/auth.service.helpers.cjs.js.map +1 -0
- package/dist/service/data.service.cjs.js +65 -0
- package/dist/service/data.service.cjs.js.map +1 -0
- package/dist/service/data.service.helpers.cjs.js +248 -0
- package/dist/service/data.service.helpers.cjs.js.map +1 -0
- package/dist/service/data.service.types.cjs.js +11 -0
- package/dist/service/data.service.types.cjs.js.map +1 -0
- package/dist/service/router.cjs.js +194 -0
- package/dist/service/router.cjs.js.map +1 -0
- package/package.json +20 -13
package/dist/index.cjs.js
CHANGED
|
@@ -2,748 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
var pathToRegexp = require('path-to-regexp');
|
|
11
|
-
var jwt = require('jsonwebtoken');
|
|
12
|
-
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
13
|
-
var zod = require('zod');
|
|
14
|
-
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
5
|
+
var router = require('./service/router.cjs.js');
|
|
6
|
+
var plugin = require('./plugin.cjs.js');
|
|
7
|
+
var permissions = require('./permission/permissions.cjs.js');
|
|
8
|
+
var conditions = require('./permission/conditions.cjs.js');
|
|
9
|
+
require('./permission/rules.cjs.js');
|
|
15
10
|
|
|
16
|
-
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
17
11
|
|
|
18
|
-
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
19
|
-
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
20
|
-
var jwt__default = /*#__PURE__*/_interopDefaultCompat(jwt);
|
|
21
12
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const dataProjectParser = (projectStatistics, organizationProjects) => {
|
|
30
|
-
const organizationData = organizationProjects.reduce((prev, next) => {
|
|
31
|
-
prev[next.uuid] = next;
|
|
32
|
-
return prev;
|
|
33
|
-
}, {});
|
|
34
|
-
const projectData = projectStatistics.reduce(
|
|
35
|
-
(prev, next) => {
|
|
36
|
-
const dependenciesCritical = next.statistics["ALERTS" /* DEPENDENCIES */].criticalSeverityVulnerabilities;
|
|
37
|
-
const dependenciesHigh = next.statistics["ALERTS" /* DEPENDENCIES */].highSeverityVulnerabilities;
|
|
38
|
-
const dependenciesMedium = next.statistics["ALERTS" /* DEPENDENCIES */].mediumSeverityVulnerabilities;
|
|
39
|
-
const dependenciesLow = next.statistics["ALERTS" /* DEPENDENCIES */].lowSeverityVulnerabilities;
|
|
40
|
-
const dependeciesTotal = dependenciesCritical + dependenciesHigh + dependenciesMedium + dependenciesLow;
|
|
41
|
-
const codeHigh = next.statistics["SAST_VULNERABILITIES_BY_SEVERITY" /* CODE */].sastHighVulnerabilities;
|
|
42
|
-
const codeMedium = next.statistics["SAST_VULNERABILITIES_BY_SEVERITY" /* CODE */].sastMediumVulnerabilities;
|
|
43
|
-
const codeLow = next.statistics["SAST_VULNERABILITIES_BY_SEVERITY" /* CODE */].sastLowVulnerabilities;
|
|
44
|
-
const codeTotal = codeHigh + codeMedium + codeLow;
|
|
45
|
-
const containersCritical = next.statistics["IMG_SECURITY" /* CONTAINERS */].imgCriticalVulnerabilities;
|
|
46
|
-
const containersHigh = next.statistics["IMG_SECURITY" /* CONTAINERS */].imgHighVulnerabilities;
|
|
47
|
-
const containersMedium = next.statistics["IMG_SECURITY" /* CONTAINERS */].imgMediumVulnerabilities;
|
|
48
|
-
const containersLow = next.statistics["IMG_SECURITY" /* CONTAINERS */].imgLowVulnerabilities;
|
|
49
|
-
const containersTotal = containersCritical + containersHigh + containersMedium + containersLow;
|
|
50
|
-
const criticalTotal = dependenciesCritical + containersCritical;
|
|
51
|
-
const highTotal = dependenciesHigh + codeHigh + containersHigh;
|
|
52
|
-
const mediumTotal = dependenciesMedium + codeMedium + containersMedium;
|
|
53
|
-
const lowTotal = dependenciesLow + codeLow + containersLow;
|
|
54
|
-
const total = dependeciesTotal + codeTotal + containersTotal;
|
|
55
|
-
const statistics = {
|
|
56
|
-
[StatisticsEngine.DEPENDENCIES]: {
|
|
57
|
-
critical: dependenciesCritical,
|
|
58
|
-
high: dependenciesHigh,
|
|
59
|
-
medium: dependenciesMedium,
|
|
60
|
-
low: dependenciesLow,
|
|
61
|
-
total: dependeciesTotal
|
|
62
|
-
},
|
|
63
|
-
[StatisticsEngine.CODE]: {
|
|
64
|
-
critical: null,
|
|
65
|
-
high: codeHigh,
|
|
66
|
-
medium: codeMedium,
|
|
67
|
-
low: codeLow,
|
|
68
|
-
total: codeTotal
|
|
69
|
-
},
|
|
70
|
-
[StatisticsEngine.CONTAINERS]: {
|
|
71
|
-
critical: containersCritical,
|
|
72
|
-
high: containersHigh,
|
|
73
|
-
medium: containersMedium,
|
|
74
|
-
low: containersLow,
|
|
75
|
-
total: containersTotal
|
|
76
|
-
},
|
|
77
|
-
critical: criticalTotal,
|
|
78
|
-
high: highTotal,
|
|
79
|
-
medium: mediumTotal,
|
|
80
|
-
low: lowTotal,
|
|
81
|
-
total
|
|
82
|
-
};
|
|
83
|
-
const project = {
|
|
84
|
-
statistics,
|
|
85
|
-
uuid: next.uuid,
|
|
86
|
-
name: next.name,
|
|
87
|
-
path: next.path,
|
|
88
|
-
entity: next.entity,
|
|
89
|
-
applicationName: organizationData[next.uuid].applicationName,
|
|
90
|
-
applicationUuid: next.applicationUuid,
|
|
91
|
-
lastScan: next.statistics["LAST_SCAN" /* LAST_SCAN */].lastScanTime,
|
|
92
|
-
languages: Object.entries(next.statistics.LIBRARY_TYPE_HISTOGRAM).sort(
|
|
93
|
-
(a, b) => b[1] - a[1]
|
|
94
|
-
)
|
|
95
|
-
};
|
|
96
|
-
prev.projectList.unshift(project);
|
|
97
|
-
return prev;
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
projectList: []
|
|
101
|
-
}
|
|
102
|
-
);
|
|
103
|
-
projectData.projectList.sort(
|
|
104
|
-
(a, b) => b.statistics.critical - a.statistics.critical
|
|
105
|
-
);
|
|
106
|
-
return projectData;
|
|
107
|
-
};
|
|
108
|
-
const parseEntityURL = (entityUrl) => {
|
|
109
|
-
try {
|
|
110
|
-
if (!entityUrl) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
const matches = entityUrl.match(
|
|
114
|
-
/https?:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(:[0-9]{1,5})?(\/.*)?/g
|
|
115
|
-
);
|
|
116
|
-
if (!matches) {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
const url = new URL(matches[0]);
|
|
120
|
-
const fn = pathToRegexp.match("/:org/:repo", { end: false });
|
|
121
|
-
return fn(url.pathname);
|
|
122
|
-
} catch (error) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
const dataMatcher = (entities, projects) => {
|
|
127
|
-
return entities.reduce(
|
|
128
|
-
(prev, next) => {
|
|
129
|
-
const entityURL = parseEntityURL(
|
|
130
|
-
next?.metadata?.annotations?.["backstage.io/source-location"]
|
|
131
|
-
);
|
|
132
|
-
if (!entityURL) {
|
|
133
|
-
return prev;
|
|
134
|
-
}
|
|
135
|
-
const project = projects.find(
|
|
136
|
-
(item) => item.path.match(/^GH_(.*)/)?.[1] === entityURL?.params.repo
|
|
137
|
-
);
|
|
138
|
-
if (!project) {
|
|
139
|
-
return prev;
|
|
140
|
-
}
|
|
141
|
-
const entity = {
|
|
142
|
-
path: entityURL.path,
|
|
143
|
-
params: entityURL.params,
|
|
144
|
-
namespace: next.metadata.namespace,
|
|
145
|
-
kind: "component",
|
|
146
|
-
source: "catalog"
|
|
147
|
-
};
|
|
148
|
-
prev.push({ ...project, entity });
|
|
149
|
-
return prev;
|
|
150
|
-
},
|
|
151
|
-
[]
|
|
152
|
-
);
|
|
153
|
-
};
|
|
154
|
-
const getIssueStatus = (engine, finding) => {
|
|
155
|
-
if (engine === StatisticsEngine.CODE) {
|
|
156
|
-
if (finding?.suppressed)
|
|
157
|
-
return "suppressed";
|
|
158
|
-
if (finding?.almIssues?.jiraPlatform?.issueStatus)
|
|
159
|
-
return "created";
|
|
160
|
-
if (finding?.reviewed)
|
|
161
|
-
return "reviewed";
|
|
162
|
-
}
|
|
163
|
-
if (engine === StatisticsEngine.DEPENDENCIES) {
|
|
164
|
-
if (finding?.findingInfo?.status === "IGNORED")
|
|
165
|
-
return "suppressed";
|
|
166
|
-
}
|
|
167
|
-
return "unreviewed";
|
|
168
|
-
};
|
|
169
|
-
const dataFindingParser = (code = [], dependencies = [], containers = []) => {
|
|
170
|
-
let codeFindings = [];
|
|
171
|
-
let dependenciesFindings = [];
|
|
172
|
-
let containersFindings = [];
|
|
173
|
-
if (code.length) {
|
|
174
|
-
codeFindings = code.map((finding) => {
|
|
175
|
-
return {
|
|
176
|
-
kind: StatisticsEngine.CODE,
|
|
177
|
-
level: finding.severity.toLowerCase(),
|
|
178
|
-
name: finding.type.cwe.title,
|
|
179
|
-
origin: `${finding.sharedStep.file}:${finding.sharedStep.line}`,
|
|
180
|
-
time: finding?.createdTime,
|
|
181
|
-
issue: {
|
|
182
|
-
issueStatus: finding.almIssues.jiraPlatform.issueStatus,
|
|
183
|
-
reporter: finding.almIssues.jiraPlatform.createdByName,
|
|
184
|
-
creationDate: finding.almIssues.jiraPlatform.createdTime,
|
|
185
|
-
ticketName: finding.almIssues.jiraPlatform.issueKey,
|
|
186
|
-
link: `${finding.almIssues.jiraPlatform.publicLink}/browse/${finding.almIssues.jiraPlatform.issueKey}`,
|
|
187
|
-
status: getIssueStatus(StatisticsEngine.CODE, finding)
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
if (dependencies.length) {
|
|
193
|
-
dependenciesFindings = dependencies.map((finding) => {
|
|
194
|
-
return {
|
|
195
|
-
kind: StatisticsEngine.DEPENDENCIES,
|
|
196
|
-
level: finding.vulnerability.severity.toLowerCase(),
|
|
197
|
-
name: finding.vulnerability.name,
|
|
198
|
-
origin: finding.component.name,
|
|
199
|
-
time: finding.vulnerability.modifiedDate,
|
|
200
|
-
issue: {
|
|
201
|
-
issueStatus: "",
|
|
202
|
-
reporter: "",
|
|
203
|
-
creationDate: "",
|
|
204
|
-
ticketName: "",
|
|
205
|
-
link: "",
|
|
206
|
-
status: getIssueStatus(StatisticsEngine.DEPENDENCIES, finding)
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
if (containers.length) {
|
|
212
|
-
containersFindings = containers.map((finding) => {
|
|
213
|
-
return {
|
|
214
|
-
kind: StatisticsEngine.CONTAINERS,
|
|
215
|
-
level: finding.severity.toLowerCase(),
|
|
216
|
-
name: finding.vulnerabilityId,
|
|
217
|
-
origin: finding.packageName,
|
|
218
|
-
time: finding.detectionDate,
|
|
219
|
-
issue: {
|
|
220
|
-
issueStatus: "",
|
|
221
|
-
reporter: "",
|
|
222
|
-
creationDate: "",
|
|
223
|
-
ticketName: "",
|
|
224
|
-
link: "",
|
|
225
|
-
status: getIssueStatus(StatisticsEngine.CONTAINERS, finding)
|
|
226
|
-
// NOTE: Currently, issue for finding in containers no exist.
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
const order = {
|
|
232
|
-
critical: 1,
|
|
233
|
-
high: 2,
|
|
234
|
-
medium: 3,
|
|
235
|
-
low: 4
|
|
236
|
-
};
|
|
237
|
-
return [...codeFindings, ...dependenciesFindings, ...containersFindings].sort(
|
|
238
|
-
(a, b) => {
|
|
239
|
-
return order[a.level] - order[b.level];
|
|
240
|
-
}
|
|
241
|
-
);
|
|
242
|
-
};
|
|
243
|
-
const parseQueryString = (href = "?") => {
|
|
244
|
-
const [, queryString] = href.split("?");
|
|
245
|
-
const queryParams = {};
|
|
246
|
-
new URLSearchParams(queryString).forEach((val, key) => {
|
|
247
|
-
queryParams[key] = val;
|
|
248
|
-
});
|
|
249
|
-
return queryParams;
|
|
250
|
-
};
|
|
251
|
-
const fetchQueryPagination = async (cb) => {
|
|
252
|
-
const defaultQueryParams = { limit: "10000", cursor: "0" };
|
|
253
|
-
const collection = [];
|
|
254
|
-
const fetchLoop = async (queryParams) => {
|
|
255
|
-
const result = await cb({ queryParams });
|
|
256
|
-
collection.push(...result.response);
|
|
257
|
-
const nextQuery = result.additionalData?.paging?.next;
|
|
258
|
-
if (nextQuery) {
|
|
259
|
-
const newQueryParams = parseQueryString(nextQuery);
|
|
260
|
-
await fetchLoop(newQueryParams);
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
await fetchLoop(defaultQueryParams);
|
|
264
|
-
return collection;
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
const caesarCipherDecrypt = (activationKey) => {
|
|
268
|
-
let tmp = "";
|
|
269
|
-
const OFFSET = 4;
|
|
270
|
-
for (let i = 0; i < activationKey.length; i++) {
|
|
271
|
-
tmp += String.fromCharCode(activationKey.charCodeAt(i) - OFFSET);
|
|
272
|
-
}
|
|
273
|
-
const reversed = tmp.split("").reverse().join("");
|
|
274
|
-
return Buffer.from(reversed, "base64").toString();
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
class MendAuthSevice {
|
|
278
|
-
static authToken = "";
|
|
279
|
-
static refreshToken = "";
|
|
280
|
-
static baseUrl = "";
|
|
281
|
-
static clientEmail = "";
|
|
282
|
-
static clientKey = "";
|
|
283
|
-
static clientUrl = "";
|
|
284
|
-
static clientName = "";
|
|
285
|
-
static clientUuid = "";
|
|
286
|
-
constructor(config) {
|
|
287
|
-
MendAuthSevice.baseUrl = config.baseUrl;
|
|
288
|
-
this.getConfig(config.activationKey);
|
|
289
|
-
}
|
|
290
|
-
getConfig(activationKey) {
|
|
291
|
-
const licenseKey = caesarCipherDecrypt(activationKey);
|
|
292
|
-
const jwtPayload = jwt__default.default.decode(licenseKey);
|
|
293
|
-
MendAuthSevice.clientEmail = jwtPayload.integratorEmail;
|
|
294
|
-
MendAuthSevice.clientKey = jwtPayload.userKey;
|
|
295
|
-
MendAuthSevice.clientUrl = jwtPayload.wsEnvUrl;
|
|
296
|
-
}
|
|
297
|
-
static async login() {
|
|
298
|
-
return post("/login" /* LOGIN */, {
|
|
299
|
-
body: {
|
|
300
|
-
email: this.clientEmail,
|
|
301
|
-
userKey: this.clientKey
|
|
302
|
-
}
|
|
303
|
-
}).then((data) => {
|
|
304
|
-
this.refreshToken = data.response.refreshToken;
|
|
305
|
-
return Promise.resolve();
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
static async refreshAccessToken() {
|
|
309
|
-
return post(
|
|
310
|
-
"/login/accessToken" /* REFRESH_TOKEN */,
|
|
311
|
-
{
|
|
312
|
-
headers: {
|
|
313
|
-
"wss-refresh-token": this.refreshToken
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
).then((data) => {
|
|
317
|
-
this.authToken = data.response.jwtToken;
|
|
318
|
-
this.clientName = data.response.orgName;
|
|
319
|
-
this.clientUuid = data.response.orgUuid;
|
|
320
|
-
return Promise.resolve();
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
static async connect() {
|
|
324
|
-
return MendAuthSevice.login().then(
|
|
325
|
-
() => MendAuthSevice.refreshAccessToken()
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
static async validateAuthToken(url) {
|
|
329
|
-
if (["/login" /* LOGIN */, "/login/accessToken" /* REFRESH_TOKEN */].includes(url)) {
|
|
330
|
-
return Promise.resolve();
|
|
331
|
-
}
|
|
332
|
-
if (!this.authToken) {
|
|
333
|
-
return this.connect();
|
|
334
|
-
}
|
|
335
|
-
const token = jwt__default.default.decode(this.authToken);
|
|
336
|
-
if (new Date(Number(`${token.exp}000`)).getTime() - Date.now() < 0) {
|
|
337
|
-
return this.connect();
|
|
338
|
-
}
|
|
339
|
-
return Promise.resolve();
|
|
340
|
-
}
|
|
341
|
-
static getAuthToken() {
|
|
342
|
-
return MendAuthSevice.authToken;
|
|
343
|
-
}
|
|
344
|
-
static getBaseUrl() {
|
|
345
|
-
return MendAuthSevice.baseUrl;
|
|
346
|
-
}
|
|
347
|
-
static getOrganizationUuid() {
|
|
348
|
-
return MendAuthSevice.clientUuid;
|
|
349
|
-
}
|
|
350
|
-
static getClientUrl() {
|
|
351
|
-
return MendAuthSevice.clientUrl;
|
|
352
|
-
}
|
|
353
|
-
static getClientName() {
|
|
354
|
-
return MendAuthSevice.clientName;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
function assembleUri(uri, params) {
|
|
359
|
-
if (!params) {
|
|
360
|
-
return uri;
|
|
361
|
-
}
|
|
362
|
-
const queryString = new URLSearchParams(params).toString();
|
|
363
|
-
return `${uri}?${queryString}`;
|
|
364
|
-
}
|
|
365
|
-
function buildHeaders(optionHeaders) {
|
|
366
|
-
const headers = new Headers();
|
|
367
|
-
headers.set("Content-Type" /* CONTENT_TYPE */, "application/json");
|
|
368
|
-
headers.set("agent-name" /* AGENT_NAME */, "pi-backstage");
|
|
369
|
-
headers.set("agent-version" /* AGENT_VERSION */, "24.8.2");
|
|
370
|
-
const authToken = MendAuthSevice.getAuthToken();
|
|
371
|
-
if (authToken) {
|
|
372
|
-
headers.set("Authorization" /* AUTH_TOKEN */, `Bearer ${authToken}`);
|
|
373
|
-
}
|
|
374
|
-
Object.keys(optionHeaders).forEach((header) => {
|
|
375
|
-
const headerValue = optionHeaders[header];
|
|
376
|
-
if (headerValue) {
|
|
377
|
-
headers.set(header, headerValue);
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
return headers;
|
|
381
|
-
}
|
|
382
|
-
function fetchRequest(method, path, opts) {
|
|
383
|
-
return MendAuthSevice.validateAuthToken(path).then(() => {
|
|
384
|
-
const { params, body, headers } = opts;
|
|
385
|
-
const url = `${MendAuthSevice.getBaseUrl()}${path}`;
|
|
386
|
-
const requestURL = params ? assembleUri(url, params) : url;
|
|
387
|
-
const requestParams = {
|
|
388
|
-
headers: buildHeaders(headers || {}),
|
|
389
|
-
method,
|
|
390
|
-
body
|
|
391
|
-
};
|
|
392
|
-
if (body) {
|
|
393
|
-
requestParams.body = typeof body === "string" ? body : JSON.stringify(body);
|
|
394
|
-
}
|
|
395
|
-
const requestObject = new Request(requestURL, requestParams);
|
|
396
|
-
return fetch(requestObject);
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
function toJson(response) {
|
|
400
|
-
if (response.status === 204 || response.body === null) {
|
|
401
|
-
return Promise.resolve({});
|
|
402
|
-
}
|
|
403
|
-
return response.json().then((json) => {
|
|
404
|
-
return response.ok ? json : Promise.reject(json);
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
const defaultOpts = {
|
|
408
|
-
body: null,
|
|
409
|
-
headers: {},
|
|
410
|
-
params: null
|
|
411
|
-
};
|
|
412
|
-
function get(url, opts = defaultOpts) {
|
|
413
|
-
return fetchRequest("GET" /* GET */, url, opts).then(toJson);
|
|
414
|
-
}
|
|
415
|
-
function post(url, opts = defaultOpts) {
|
|
416
|
-
return fetchRequest("POST" /* POST */, url, opts).then(toJson);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
class MendDataService extends MendAuthSevice {
|
|
420
|
-
async getOrganizationProject({
|
|
421
|
-
queryParams
|
|
422
|
-
}) {
|
|
423
|
-
return get(`/orgs/${MendAuthSevice.getOrganizationUuid()}/projects`, {
|
|
424
|
-
params: {
|
|
425
|
-
...queryParams
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
async getProjectStatistics({
|
|
430
|
-
queryParams,
|
|
431
|
-
bodyParams
|
|
432
|
-
}) {
|
|
433
|
-
return post(
|
|
434
|
-
`/orgs/${MendAuthSevice.getOrganizationUuid()}/projects/summaries`,
|
|
435
|
-
{
|
|
436
|
-
params: {
|
|
437
|
-
...queryParams
|
|
438
|
-
},
|
|
439
|
-
body: {
|
|
440
|
-
...bodyParams
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
async getCodeFinding({
|
|
446
|
-
pathParams,
|
|
447
|
-
queryParams
|
|
448
|
-
}) {
|
|
449
|
-
return get(`/projects/${pathParams.uuid}/code/findings`, {
|
|
450
|
-
params: {
|
|
451
|
-
...queryParams
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
async getDependenciesFinding({
|
|
456
|
-
pathParams,
|
|
457
|
-
queryParams
|
|
458
|
-
}) {
|
|
459
|
-
return get(`/projects/${pathParams.uuid}/dependencies/findings/security`, {
|
|
460
|
-
params: {
|
|
461
|
-
...queryParams
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
async getContainersFinding({
|
|
466
|
-
pathParams,
|
|
467
|
-
queryParams
|
|
468
|
-
}) {
|
|
469
|
-
return get(`/projects/${pathParams.uuid}/images/findings/security`, {
|
|
470
|
-
params: {
|
|
471
|
-
...queryParams
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
var RESOURCE_TYPE = /* @__PURE__ */ ((RESOURCE_TYPE2) => {
|
|
478
|
-
RESOURCE_TYPE2["PROJECT"] = "mend-project";
|
|
479
|
-
return RESOURCE_TYPE2;
|
|
480
|
-
})(RESOURCE_TYPE || {});
|
|
481
|
-
const mendReadPermission = pluginPermissionCommon.createPermission({
|
|
482
|
-
name: "mend.project.read",
|
|
483
|
-
attributes: { action: "read" },
|
|
484
|
-
resourceType: "mend-project" /* PROJECT */
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
const createProjectPermissionRule = pluginPermissionNode.makeCreatePermissionRule();
|
|
488
|
-
const filter = createProjectPermissionRule({
|
|
489
|
-
name: "filter",
|
|
490
|
-
description: "Should allow read-only access to filtered projects.",
|
|
491
|
-
resourceType: RESOURCE_TYPE.PROJECT,
|
|
492
|
-
paramsSchema: zod.z.object({
|
|
493
|
-
ids: zod.z.string().array().describe("Project ID to match resource"),
|
|
494
|
-
exclude: zod.z.boolean().optional().describe("Exclude or include project")
|
|
495
|
-
}),
|
|
496
|
-
apply: (resource, { ids, exclude = true }) => {
|
|
497
|
-
return exclude ? !ids.includes(resource.resourceRef) : ids.includes(resource.resourceRef);
|
|
498
|
-
},
|
|
499
|
-
toQuery: ({ ids, exclude = true }) => {
|
|
500
|
-
return {
|
|
501
|
-
ids,
|
|
502
|
-
exclude
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
const rules = { filter };
|
|
507
|
-
|
|
508
|
-
const { conditions, createConditionalDecision } = pluginPermissionNode.createConditionExports({
|
|
509
|
-
pluginId: "mend",
|
|
510
|
-
resourceType: RESOURCE_TYPE.PROJECT,
|
|
511
|
-
rules
|
|
512
|
-
});
|
|
513
|
-
const mendConditions = conditions;
|
|
514
|
-
const createMendProjectConditionalDecision = createConditionalDecision;
|
|
515
|
-
const permissionIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
|
|
516
|
-
permissions: [mendReadPermission],
|
|
517
|
-
getResources: async (resourceRefs) => {
|
|
518
|
-
return resourceRefs.map((resourceRef) => {
|
|
519
|
-
return {
|
|
520
|
-
permission: mendReadPermission,
|
|
521
|
-
resourceRef
|
|
522
|
-
};
|
|
523
|
-
});
|
|
524
|
-
},
|
|
525
|
-
resourceType: RESOURCE_TYPE.PROJECT,
|
|
526
|
-
rules: Object.values(rules)
|
|
527
|
-
});
|
|
528
|
-
const transformConditions = pluginPermissionNode.createConditionTransformer(Object.values(rules));
|
|
529
|
-
|
|
530
|
-
async function createRouter(options) {
|
|
531
|
-
const { logger, config, discovery, auth, httpAuth, permissions } = options;
|
|
532
|
-
const router = Router__default.default();
|
|
533
|
-
router.use(express__default.default.json());
|
|
534
|
-
router.use(permissionIntegrationRouter);
|
|
535
|
-
const checkForAuth = (_request, response, next) => {
|
|
536
|
-
if (MendAuthSevice.getAuthToken()) {
|
|
537
|
-
next();
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
MendAuthSevice.connect().then(next).catch(() => {
|
|
541
|
-
response.status(401).json({ error: "Oops! Unauthorized" });
|
|
542
|
-
});
|
|
543
|
-
};
|
|
544
|
-
const baseUrl = config.getString("mend.baseUrl");
|
|
545
|
-
const activationKey = config.getString("mend.activationKey");
|
|
546
|
-
const mendDataService = new MendDataService({
|
|
547
|
-
baseUrl,
|
|
548
|
-
activationKey
|
|
549
|
-
});
|
|
550
|
-
const catalogClient$1 = new catalogClient.CatalogClient({ discoveryApi: discovery });
|
|
551
|
-
router.get("/project" /* PROJECT */, checkForAuth, async (request, response) => {
|
|
552
|
-
try {
|
|
553
|
-
const credentials = await httpAuth.credentials(request);
|
|
554
|
-
const { token } = await auth.getPluginRequestToken({
|
|
555
|
-
onBehalfOf: credentials,
|
|
556
|
-
targetPluginId: "plugin.catalog.service"
|
|
557
|
-
});
|
|
558
|
-
const results = await Promise.all([
|
|
559
|
-
catalogClient$1.getEntities(
|
|
560
|
-
{ filter: [{ kind: ["Component"] }] },
|
|
561
|
-
{ token }
|
|
562
|
-
),
|
|
563
|
-
fetchQueryPagination(
|
|
564
|
-
mendDataService.getProjectStatistics
|
|
565
|
-
),
|
|
566
|
-
fetchQueryPagination(
|
|
567
|
-
mendDataService.getOrganizationProject
|
|
568
|
-
)
|
|
569
|
-
]);
|
|
570
|
-
const decision = (await permissions.authorizeConditional(
|
|
571
|
-
[{ permission: mendReadPermission }],
|
|
572
|
-
{
|
|
573
|
-
credentials
|
|
574
|
-
}
|
|
575
|
-
))[0];
|
|
576
|
-
let items;
|
|
577
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
578
|
-
const filter = transformConditions(decision.conditions);
|
|
579
|
-
items = results[1].filter(
|
|
580
|
-
(item) => filter?.exclude ? !filter.ids.includes(item.uuid) : filter.ids.includes(item.uuid)
|
|
581
|
-
);
|
|
582
|
-
}
|
|
583
|
-
const data = dataMatcher(results[0].items, items || results[1]);
|
|
584
|
-
const projects = dataProjectParser(data, results[2]);
|
|
585
|
-
response.json({
|
|
586
|
-
...projects,
|
|
587
|
-
clientUrl: MendAuthSevice.getClientUrl(),
|
|
588
|
-
clientName: MendAuthSevice.getClientName()
|
|
589
|
-
});
|
|
590
|
-
} catch (error) {
|
|
591
|
-
logger.error("/project", error);
|
|
592
|
-
response.status(500).json({ error: "Oops! Please try again later." });
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
router.post("/finding" /* FINDING */, checkForAuth, async (request, response) => {
|
|
596
|
-
try {
|
|
597
|
-
const credentials = await httpAuth.credentials(request);
|
|
598
|
-
const { token } = await auth.getPluginRequestToken({
|
|
599
|
-
onBehalfOf: credentials,
|
|
600
|
-
targetPluginId: "plugin.catalog.service"
|
|
601
|
-
});
|
|
602
|
-
const uid = request.body.uid;
|
|
603
|
-
if (!uid) {
|
|
604
|
-
response.status(401).json({ error: "Oops! No UUID provided" });
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
const projectResult = await Promise.all([
|
|
608
|
-
catalogClient$1.getEntities(
|
|
609
|
-
{ filter: [{ "metadata.uid": uid }] },
|
|
610
|
-
{ token }
|
|
611
|
-
),
|
|
612
|
-
fetchQueryPagination(
|
|
613
|
-
mendDataService.getProjectStatistics
|
|
614
|
-
),
|
|
615
|
-
fetchQueryPagination(
|
|
616
|
-
mendDataService.getOrganizationProject
|
|
617
|
-
)
|
|
618
|
-
]);
|
|
619
|
-
const decision = (await permissions.authorizeConditional(
|
|
620
|
-
[{ permission: mendReadPermission }],
|
|
621
|
-
{
|
|
622
|
-
credentials
|
|
623
|
-
}
|
|
624
|
-
))[0];
|
|
625
|
-
let items;
|
|
626
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
627
|
-
const filter = transformConditions(decision.conditions);
|
|
628
|
-
items = projectResult[1].filter(
|
|
629
|
-
(item) => filter?.exclude ? !filter.ids.includes(item.uuid) : filter.ids.includes(item.uuid)
|
|
630
|
-
);
|
|
631
|
-
}
|
|
632
|
-
const data = dataMatcher(
|
|
633
|
-
projectResult[0].items,
|
|
634
|
-
items || projectResult[1]
|
|
635
|
-
);
|
|
636
|
-
if (!data.length) {
|
|
637
|
-
response.json({
|
|
638
|
-
findingList: [],
|
|
639
|
-
projectName: "",
|
|
640
|
-
projectUuid: "",
|
|
641
|
-
clientUrl: MendAuthSevice.getClientUrl(),
|
|
642
|
-
clientName: MendAuthSevice.getClientName()
|
|
643
|
-
});
|
|
644
|
-
return;
|
|
645
|
-
}
|
|
646
|
-
const params = {
|
|
647
|
-
pathParams: {
|
|
648
|
-
uuid: data[0].uuid
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
const findingResult = await Promise.all([
|
|
652
|
-
fetchQueryPagination(
|
|
653
|
-
(queryParam) => mendDataService.getCodeFinding({
|
|
654
|
-
...params,
|
|
655
|
-
...queryParam
|
|
656
|
-
})
|
|
657
|
-
),
|
|
658
|
-
fetchQueryPagination(
|
|
659
|
-
(queryParam) => mendDataService.getDependenciesFinding({
|
|
660
|
-
...params,
|
|
661
|
-
...queryParam
|
|
662
|
-
})
|
|
663
|
-
),
|
|
664
|
-
fetchQueryPagination(
|
|
665
|
-
(queryParam) => mendDataService.getContainersFinding({
|
|
666
|
-
...params,
|
|
667
|
-
...queryParam
|
|
668
|
-
})
|
|
669
|
-
)
|
|
670
|
-
]);
|
|
671
|
-
const project = dataProjectParser(data, projectResult[2]);
|
|
672
|
-
const findingList = dataFindingParser(
|
|
673
|
-
findingResult[0].filter((item) => !item.suppressed),
|
|
674
|
-
// NOTE: Do not show suppressed item
|
|
675
|
-
findingResult[1].filter(
|
|
676
|
-
(item) => !(item.findingInfo.status === "IGNORED")
|
|
677
|
-
),
|
|
678
|
-
// NOTE: Do not show ignored item
|
|
679
|
-
findingResult[2]
|
|
680
|
-
// ESC-51: Follow Jira activity
|
|
681
|
-
);
|
|
682
|
-
response.json({
|
|
683
|
-
findingList,
|
|
684
|
-
projectName: project.projectList[0].entity.params.repo,
|
|
685
|
-
projectUuid: project.projectList[0].uuid,
|
|
686
|
-
clientUrl: MendAuthSevice.getClientUrl(),
|
|
687
|
-
clientName: MendAuthSevice.getClientName()
|
|
688
|
-
});
|
|
689
|
-
} catch (error) {
|
|
690
|
-
logger.error("/finding", error);
|
|
691
|
-
response.status(500).json({ error: "Oops! Please try again later." });
|
|
692
|
-
}
|
|
693
|
-
});
|
|
694
|
-
router.get("/health", (_, response) => {
|
|
695
|
-
logger.info("PONG!");
|
|
696
|
-
response.json({ status: "ok" });
|
|
697
|
-
});
|
|
698
|
-
const middleware = rootHttpRouter.MiddlewareFactory.create({ logger, config });
|
|
699
|
-
router.use(middleware.error());
|
|
700
|
-
return router;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
const mendPlugin = backendPluginApi.createBackendPlugin({
|
|
704
|
-
pluginId: "mend",
|
|
705
|
-
register(env) {
|
|
706
|
-
env.registerInit({
|
|
707
|
-
deps: {
|
|
708
|
-
auth: backendPluginApi.coreServices.auth,
|
|
709
|
-
config: backendPluginApi.coreServices.rootConfig,
|
|
710
|
-
discovery: backendPluginApi.coreServices.discovery,
|
|
711
|
-
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
712
|
-
httpRouter: backendPluginApi.coreServices.httpRouter,
|
|
713
|
-
logger: backendPluginApi.coreServices.logger,
|
|
714
|
-
permissions: backendPluginApi.coreServices.permissions
|
|
715
|
-
},
|
|
716
|
-
async init({
|
|
717
|
-
auth,
|
|
718
|
-
config,
|
|
719
|
-
discovery,
|
|
720
|
-
httpAuth,
|
|
721
|
-
httpRouter,
|
|
722
|
-
logger,
|
|
723
|
-
permissions
|
|
724
|
-
}) {
|
|
725
|
-
httpRouter.use(
|
|
726
|
-
await createRouter({
|
|
727
|
-
auth,
|
|
728
|
-
config,
|
|
729
|
-
discovery,
|
|
730
|
-
httpAuth,
|
|
731
|
-
logger,
|
|
732
|
-
permissions
|
|
733
|
-
})
|
|
734
|
-
);
|
|
735
|
-
httpRouter.addAuthPolicy({
|
|
736
|
-
path: "/health",
|
|
737
|
-
allow: "unauthenticated"
|
|
738
|
-
});
|
|
739
|
-
}
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
});
|
|
743
|
-
|
|
744
|
-
exports.createMendProjectConditionalDecision = createMendProjectConditionalDecision;
|
|
745
|
-
exports.createRouter = createRouter;
|
|
746
|
-
exports.default = mendPlugin;
|
|
747
|
-
exports.mendConditions = mendConditions;
|
|
748
|
-
exports.mendReadPermission = mendReadPermission;
|
|
13
|
+
exports.createRouter = router.createRouter;
|
|
14
|
+
exports.default = plugin.mendPlugin;
|
|
15
|
+
exports.RESOURCE_TYPE = permissions.RESOURCE_TYPE;
|
|
16
|
+
exports.mendReadPermission = permissions.mendReadPermission;
|
|
17
|
+
exports.createMendProjectConditionalDecision = conditions.createMendProjectConditionalDecision;
|
|
18
|
+
exports.mendConditions = conditions.mendConditions;
|
|
749
19
|
//# sourceMappingURL=index.cjs.js.map
|