@backstage-community/plugin-mend-backend 0.10.0 → 1.0.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 +71 -0
- package/README.md +22 -34
- package/config.d.ts +37 -1
- package/dist/api/index.cjs.js.map +1 -1
- package/dist/constants.cjs.js.map +1 -1
- package/dist/index.cjs.js +0 -7
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -36
- package/dist/plugin.cjs.js +3 -13
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/service/auth.service.cjs.js +44 -9
- package/dist/service/auth.service.cjs.js.map +1 -1
- package/dist/service/auth.service.helpers.cjs.js.map +1 -1
- package/dist/service/data.service.cjs.js.map +1 -1
- package/dist/service/data.service.helpers.cjs.js.map +1 -1
- package/dist/service/data.service.types.cjs.js.map +1 -1
- package/dist/service/router.cjs.js +44 -44
- package/dist/service/router.cjs.js.map +1 -1
- package/package.json +4 -3
- package/dist/permission/conditions.cjs.js +0 -33
- package/dist/permission/conditions.cjs.js.map +0 -1
- package/dist/permission/permissions.cjs.js +0 -17
- package/dist/permission/permissions.cjs.js.map +0 -1
- package/dist/permission/rules.cjs.js +0 -31
- package/dist/permission/rules.cjs.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
# @backstage-community/plugin-mend-backend
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- bc3cc34: - Updated Frontend system to provide the Backstage Portal Support
|
|
8
|
+
|
|
9
|
+
- **BREAKING** The permission-policy-based project filtering has been removed in favor of configuration-based `mend.permissionControl`. This is only applicable if you previously used the Mend permissions in your `PermissionPolicy` to control which project IDs are visible.
|
|
10
|
+
|
|
11
|
+
Previously, you might have configured project filtering like this in your organization policy:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// ... other imports here
|
|
15
|
+
import {
|
|
16
|
+
mendReadPermission,
|
|
17
|
+
mendConditions,
|
|
18
|
+
createMendProjectConditionalDecision,
|
|
19
|
+
} from '@backstage-community/plugin-mend-backend';
|
|
20
|
+
|
|
21
|
+
export class OrganizationPolicy implements PermissionPolicy {
|
|
22
|
+
async handle(
|
|
23
|
+
request: PolicyQuery,
|
|
24
|
+
user?: BackstageIdentityResponse,
|
|
25
|
+
): Promise<PolicyDecision> {
|
|
26
|
+
if (isPermission(request.permission, mendReadPermission)) {
|
|
27
|
+
return createMendProjectConditionalDecision(
|
|
28
|
+
request.permission,
|
|
29
|
+
mendConditions.filter({
|
|
30
|
+
ids: [], // List of project ids
|
|
31
|
+
exclude: true, // Default
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ... other conditions
|
|
37
|
+
return {
|
|
38
|
+
result: AuthorizeResult.ALLOW,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
You must now configure the equivalent behavior via `app-config.yaml`:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
mend:
|
|
48
|
+
activationKey: ${MEND_ACTIVATION_KEY}
|
|
49
|
+
permissionControl:
|
|
50
|
+
ids:
|
|
51
|
+
- <project-uuid-1> # Project UUID to filter
|
|
52
|
+
- <project-uuid-2> # Another project UUID
|
|
53
|
+
exclude: true # true = blocklist (exclude these), false = allowlist (only these)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Migration steps:
|
|
57
|
+
|
|
58
|
+
1. Copy the same `ids` list and `exclude` value you used in your permission policy into the `mend.permissionControl` configuration in `app-config.yaml`.
|
|
59
|
+
2. Remove the permission-related code (such as `mendReadPermission`, `mendConditions`, and `createMendProjectConditionalDecision` usage) from your custom permission policy file (for example, `packages/backend/src/plugins/permission.ts`).
|
|
60
|
+
3. Remove the Mend permission imports from `packages/backend/src/index.ts` and ensure your default policy continues to allow access (for example, by using an allow-all-policy if no other policy is defined).
|
|
61
|
+
|
|
62
|
+
```diff
|
|
63
|
+
backend.add(import('@backstage/plugin-permission-backend'));
|
|
64
|
+
+ backend.add(
|
|
65
|
+
+ import('@backstage/plugin-permission-backend-module-allow-all-policy'),
|
|
66
|
+
+ );
|
|
67
|
+
- backend.add(import('./plugins/permission'));
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Patch Changes
|
|
71
|
+
|
|
72
|
+
- b133c9d: Updated dependency `@types/supertest` to `^6.0.0`.
|
|
73
|
+
|
|
3
74
|
## 0.10.0
|
|
4
75
|
|
|
5
76
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -9,46 +9,34 @@ In your `packages/backend/src/index.ts` file:
|
|
|
9
9
|
backend.add(import('@backstage-community/plugin-mend-backend'));
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
###
|
|
12
|
+
### Permission Control (optional)
|
|
13
13
|
|
|
14
|
-
The plugin
|
|
14
|
+
The plugin supports configuration-based permission control to filter which projects are visible to users.
|
|
15
15
|
|
|
16
|
-
- Provide a list of project IDs
|
|
17
|
-
- Use the `exclude` property to
|
|
16
|
+
- Provide a list of project IDs in the configuration to filter projects.
|
|
17
|
+
- Use the `exclude` property to control the filtering behavior:
|
|
18
|
+
- `true` (blocklist mode): Show all projects EXCEPT those in the list
|
|
19
|
+
- `false` (allowlist mode): Show ONLY projects in the list
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
Add the following configuration to your `app-config.yaml`:
|
|
20
22
|
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export class OrganizationPolicy implements PermissionPolicy {
|
|
30
|
-
async handle(
|
|
31
|
-
request: PolicyQuery,
|
|
32
|
-
user?: BackstageIdentityResponse,
|
|
33
|
-
): Promise<PolicyDecision> {
|
|
34
|
-
if (isPermission(request.permission, mendReadPermission)) {
|
|
35
|
-
return createMendProjectConditionalDecision(
|
|
36
|
-
request.permission,
|
|
37
|
-
mendConditions.filter({
|
|
38
|
-
ids: [], // List of project id
|
|
39
|
-
exclude: true, // Default
|
|
40
|
-
}),
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
// ... other conditions
|
|
44
|
-
return {
|
|
45
|
-
result: AuthorizeResult.ALLOW,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
// ...
|
|
23
|
+
```yaml
|
|
24
|
+
mend:
|
|
25
|
+
activationKey: ${MEND_ACTIVATION_KEY}
|
|
26
|
+
permissionControl:
|
|
27
|
+
ids:
|
|
28
|
+
- <project-uuid-1> # Project UUID to filter
|
|
29
|
+
- <project-uuid-2> # Another project UUID
|
|
30
|
+
exclude: true # Set to true for blocklist mode, false for allowlist mode
|
|
50
31
|
```
|
|
51
32
|
|
|
33
|
+
**Configuration Options:**
|
|
34
|
+
|
|
35
|
+
- `ids`: Array of project UUIDs to include or exclude
|
|
36
|
+
- `exclude`: Boolean flag (default: `true`)
|
|
37
|
+
- `true`: Exclude the listed projects (blocklist)
|
|
38
|
+
- `false`: Only show the listed projects (allowlist)
|
|
39
|
+
|
|
52
40
|
**Add the Mend.io frontend plugin**
|
|
53
41
|
|
|
54
42
|
See the [mend frontend plugin instructions](../mend/README.md).
|
package/config.d.ts
CHANGED
|
@@ -1,8 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
1
16
|
export type Config = {
|
|
2
|
-
mend
|
|
17
|
+
mend?: {
|
|
3
18
|
/**
|
|
4
19
|
* @visibility secret
|
|
5
20
|
*/
|
|
6
21
|
activationKey: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Permission control configuration for entity access.
|
|
25
|
+
* Controls which entities users can view Apiiro data for.
|
|
26
|
+
* @visibility frontend
|
|
27
|
+
*/
|
|
28
|
+
permissionControl?: {
|
|
29
|
+
/**
|
|
30
|
+
* List of project ids for that you want the permission control
|
|
31
|
+
*/
|
|
32
|
+
ids: string[];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Determines the permission control mode:
|
|
36
|
+
* - true (blocklist mode): Allow all project ids EXCEPT those listed in ids
|
|
37
|
+
* - false (allowlist mode): Deny all project ids EXCEPT those listed in ids
|
|
38
|
+
*
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
exclude?: boolean;
|
|
42
|
+
};
|
|
7
43
|
};
|
|
8
44
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../src/api/index.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../src/api/index.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { MendAuthSevice } from '../service/auth.service';\n\nexport type QueryParams = Record<string, string>;\n\ntype RequestHeaders = Record<string, string>;\n\nenum ApiHeaders {\n AUTH_TOKEN = 'Authorization',\n CONTENT_TYPE = 'Content-Type',\n AGENT_NAME = 'agent-name',\n AGENT_VERSION = 'agent-version',\n}\n\nenum REQUEST_METHOD {\n GET = 'GET',\n POST = 'POST',\n PUT = 'PUT',\n DELETE = 'DELETE',\n}\n\ninterface RequestOptions {\n body?: any;\n headers?: RequestHeaders;\n params?: Record<string, any> | null;\n}\n\nfunction assembleUri(uri: string, params?: QueryParams): string {\n if (!params) {\n return uri;\n }\n\n const queryString = new URLSearchParams(params).toString();\n return `${uri}?${queryString}`;\n}\n\nfunction buildHeaders(optionHeaders: RequestHeaders): Headers {\n const headers = new Headers();\n headers.set(ApiHeaders.CONTENT_TYPE, 'application/json');\n headers.set(ApiHeaders.AGENT_NAME, 'pi-backstage');\n headers.set(ApiHeaders.AGENT_VERSION, '24.8.2');\n\n const authToken = MendAuthSevice.getAuthToken();\n\n if (authToken) {\n headers.set(ApiHeaders.AUTH_TOKEN, `Bearer ${authToken}`);\n }\n\n Object.keys(optionHeaders).forEach(header => {\n const headerValue = optionHeaders[header];\n if (headerValue) {\n headers.set(header, headerValue);\n }\n });\n\n return headers;\n}\n\nfunction fetchRequest(\n method: REQUEST_METHOD,\n path: string,\n opts: RequestOptions,\n): Promise<any> {\n return MendAuthSevice.validateAuthToken(path).then(() => {\n const { params, body, headers } = opts;\n\n const url = `${MendAuthSevice.getBaseUrl()}${path}`;\n const requestURL = params ? assembleUri(url, params) : url;\n\n const requestParams = {\n headers: buildHeaders(headers || {}),\n method,\n body,\n };\n\n if (body) {\n requestParams.body =\n typeof body === 'string' ? body : JSON.stringify(body);\n }\n\n const requestObject: Request = new Request(requestURL, requestParams);\n\n return fetch(requestObject);\n });\n}\n\nfunction toJson(response: Response): Promise<any> {\n if (response.status === 204 || response.body === null) {\n return Promise.resolve({});\n }\n\n return response.json().then(json => {\n return response.ok ? json : Promise.reject(response);\n });\n}\n\nconst defaultOpts: RequestOptions = {\n body: null,\n headers: {},\n params: null,\n};\n\nexport function get<T>(\n url: string,\n opts: RequestOptions = defaultOpts,\n): Promise<T> {\n return fetchRequest(REQUEST_METHOD.GET, url, opts).then(toJson);\n}\n\nexport function post<T>(\n url: string,\n opts: RequestOptions = defaultOpts,\n): Promise<T> {\n return fetchRequest(REQUEST_METHOD.POST, url, opts).then(toJson);\n}\n\nexport function put<T>(\n url: string,\n opts: RequestOptions = defaultOpts,\n): Promise<T> {\n return fetchRequest(REQUEST_METHOD.PUT, url, opts).then(toJson);\n}\n\nexport function remove<T>(\n url: string,\n opts: RequestOptions = defaultOpts,\n): Promise<T> {\n return fetchRequest(REQUEST_METHOD.DELETE, url, opts).then(toJson);\n}\n"],"names":["MendAuthSevice"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.cjs.js","sources":["../src/constants.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"constants.cjs.js","sources":["../src/constants.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport const MEND_API_VERSION: string = 'v3.0';\n\nexport const AZURE_HOST_NAME: string = 'dev.azure.com';\n"],"names":[],"mappings":";;AAgBO,MAAM,gBAAA,GAA2B;AAEjC,MAAM,eAAA,GAA0B;;;;;"}
|
package/dist/index.cjs.js
CHANGED
|
@@ -4,16 +4,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var router = require('./service/router.cjs.js');
|
|
6
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');
|
|
10
7
|
|
|
11
8
|
|
|
12
9
|
|
|
13
10
|
exports.createRouter = router.createRouter;
|
|
14
11
|
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;
|
|
19
12
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
2
|
-
import * as _backstage_plugin_permission_common from '@backstage/plugin-permission-common';
|
|
3
|
-
import * as _backstage_plugin_permission_node from '@backstage/plugin-permission-node';
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* mendPlugin backend plugin
|
|
@@ -9,37 +7,4 @@ import * as _backstage_plugin_permission_node from '@backstage/plugin-permission
|
|
|
9
7
|
*/
|
|
10
8
|
declare const mendPlugin: _backstage_backend_plugin_api.BackendFeature;
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
declare enum RESOURCE_TYPE {
|
|
14
|
-
PROJECT = "mend-project"
|
|
15
|
-
}
|
|
16
|
-
/** @public */
|
|
17
|
-
declare const mendReadPermission: _backstage_plugin_permission_common.ResourcePermission<RESOURCE_TYPE>;
|
|
18
|
-
|
|
19
|
-
/** @public */
|
|
20
|
-
type FilterProps = {
|
|
21
|
-
ids: string[];
|
|
22
|
-
exclude?: boolean;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/** @public */
|
|
26
|
-
declare const mendConditions: _backstage_plugin_permission_node.Conditions<{
|
|
27
|
-
filter: _backstage_plugin_permission_node.PermissionRule<{
|
|
28
|
-
permission: {
|
|
29
|
-
type: string;
|
|
30
|
-
name: string;
|
|
31
|
-
attributes: {
|
|
32
|
-
action?: "update" | "read" | "delete" | "create" | undefined;
|
|
33
|
-
};
|
|
34
|
-
resourceType: RESOURCE_TYPE;
|
|
35
|
-
};
|
|
36
|
-
resourceRef: string;
|
|
37
|
-
}, FilterProps, RESOURCE_TYPE, {
|
|
38
|
-
ids: string[];
|
|
39
|
-
exclude?: boolean | undefined;
|
|
40
|
-
}>;
|
|
41
|
-
}>;
|
|
42
|
-
/** @public */
|
|
43
|
-
declare const createMendProjectConditionalDecision: (permission: _backstage_plugin_permission_common.ResourcePermission<RESOURCE_TYPE>, conditions: _backstage_plugin_permission_common.PermissionCriteria<_backstage_plugin_permission_common.PermissionCondition<RESOURCE_TYPE>>) => _backstage_plugin_permission_common.ConditionalPolicyDecision;
|
|
44
|
-
|
|
45
|
-
export { type FilterProps, RESOURCE_TYPE, createMendProjectConditionalDecision, mendPlugin as default, mendConditions, mendReadPermission };
|
|
10
|
+
export { mendPlugin as default };
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -13,26 +13,16 @@ const mendPlugin = backendPluginApi.createBackendPlugin({
|
|
|
13
13
|
discovery: backendPluginApi.coreServices.discovery,
|
|
14
14
|
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
15
15
|
httpRouter: backendPluginApi.coreServices.httpRouter,
|
|
16
|
-
logger: backendPluginApi.coreServices.logger
|
|
17
|
-
permissions: backendPluginApi.coreServices.permissions
|
|
16
|
+
logger: backendPluginApi.coreServices.logger
|
|
18
17
|
},
|
|
19
|
-
async init({
|
|
20
|
-
auth,
|
|
21
|
-
config,
|
|
22
|
-
discovery,
|
|
23
|
-
httpAuth,
|
|
24
|
-
httpRouter,
|
|
25
|
-
logger,
|
|
26
|
-
permissions
|
|
27
|
-
}) {
|
|
18
|
+
async init({ auth, config, discovery, httpAuth, httpRouter, logger }) {
|
|
28
19
|
httpRouter.use(
|
|
29
20
|
await router.createRouter({
|
|
30
21
|
auth,
|
|
31
22
|
config,
|
|
32
23
|
discovery,
|
|
33
24
|
httpAuth,
|
|
34
|
-
logger
|
|
35
|
-
permissions
|
|
25
|
+
logger
|
|
36
26
|
})
|
|
37
27
|
);
|
|
38
28
|
httpRouter.addAuthPolicy({
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\n\n/**\n * mendPlugin backend plugin\n *\n * @public\n */\nexport const mendPlugin = createBackendPlugin({\n pluginId: 'mend',\n register(env) {\n env.registerInit({\n deps: {\n auth: coreServices.auth,\n config: coreServices.rootConfig,\n discovery: coreServices.discovery,\n httpAuth: coreServices.httpAuth,\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n },\n async init({ auth, config, discovery, httpAuth, httpRouter, logger }) {\n httpRouter.use(\n await createRouter({\n auth,\n config,\n discovery,\n httpAuth,\n logger,\n }),\n );\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["createBackendPlugin","coreServices","createRouter"],"mappings":";;;;;AA0BO,MAAM,aAAaA,oCAAA,CAAoB;AAAA,EAC5C,QAAA,EAAU,MAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,MAAMC,6BAAA,CAAa,IAAA;AAAA,QACnB,QAAQA,6BAAA,CAAa,UAAA;AAAA,QACrB,WAAWA,6BAAA,CAAa,SAAA;AAAA,QACxB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,YAAYA,6BAAA,CAAa,UAAA;AAAA,QACzB,QAAQA,6BAAA,CAAa;AAAA,OACvB;AAAA,MACA,MAAM,KAAK,EAAE,IAAA,EAAM,QAAQ,SAAA,EAAW,QAAA,EAAU,UAAA,EAAY,MAAA,EAAO,EAAG;AACpE,QAAA,UAAA,CAAW,GAAA;AAAA,UACT,MAAMC,mBAAA,CAAa;AAAA,YACjB,IAAA;AAAA,YACA,MAAA;AAAA,YACA,SAAA;AAAA,YACA,QAAA;AAAA,YACA;AAAA,WACD;AAAA,SACH;AACA,QAAA,UAAA,CAAW,aAAA,CAAc;AAAA,UACvB,IAAA,EAAM,SAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AAAA,MACH;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
|
|
@@ -17,30 +17,54 @@ class MendAuthSevice {
|
|
|
17
17
|
static clientUrl = "";
|
|
18
18
|
static clientName = "";
|
|
19
19
|
static clientUuid = "";
|
|
20
|
+
static configured = false;
|
|
21
|
+
static configurationError = "";
|
|
22
|
+
static logger;
|
|
20
23
|
constructor(config) {
|
|
21
|
-
|
|
24
|
+
MendAuthSevice.init(config);
|
|
22
25
|
}
|
|
23
|
-
|
|
26
|
+
static init(config) {
|
|
27
|
+
MendAuthSevice.logger = config.logger;
|
|
28
|
+
MendAuthSevice.configure(config.apiVersion, config.activationKey);
|
|
29
|
+
}
|
|
30
|
+
static configure(apiVersion, activationKey) {
|
|
31
|
+
MendAuthSevice.configured = false;
|
|
32
|
+
MendAuthSevice.configurationError = "";
|
|
33
|
+
MendAuthSevice.baseUrl = "";
|
|
34
|
+
MendAuthSevice.clientEmail = "";
|
|
35
|
+
MendAuthSevice.clientKey = "";
|
|
36
|
+
MendAuthSevice.clientUrl = "";
|
|
37
|
+
if (!activationKey) {
|
|
38
|
+
MendAuthSevice.configurationError = "Mend activation key is not configured. Please set the Activation Key in the configuration file.";
|
|
39
|
+
MendAuthSevice.logger?.warn(MendAuthSevice.configurationError);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
24
42
|
try {
|
|
25
43
|
const licenseKey = auth_service_helpers.caesarCipherDecrypt(activationKey);
|
|
26
44
|
const decoded = jwt__default.default.decode(licenseKey);
|
|
27
45
|
if (!decoded || typeof decoded !== "object") {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
46
|
+
MendAuthSevice.configurationError = "Invalid activation key. Please provide the valid Activation Key.";
|
|
47
|
+
MendAuthSevice.logger.error(MendAuthSevice.configurationError);
|
|
48
|
+
return;
|
|
31
49
|
}
|
|
32
50
|
const jwtPayload = decoded;
|
|
33
51
|
const { wsEnvUrl, integratorEmail, userKey } = jwtPayload || {};
|
|
34
52
|
if (!wsEnvUrl || typeof wsEnvUrl !== "string" || !integratorEmail || typeof integratorEmail !== "string" || !userKey || typeof userKey !== "string") {
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
MendAuthSevice.configurationError = "Invalid activation key. Please provide the valid Activation Key.";
|
|
54
|
+
MendAuthSevice.logger.error(
|
|
55
|
+
"Invalid activation key: missing required fields (wsEnvUrl, integratorEmail, userKey)."
|
|
37
56
|
);
|
|
57
|
+
return;
|
|
38
58
|
}
|
|
39
59
|
let baseUrl;
|
|
40
60
|
try {
|
|
41
61
|
baseUrl = new URL(wsEnvUrl);
|
|
42
62
|
} catch {
|
|
43
|
-
|
|
63
|
+
MendAuthSevice.configurationError = "Invalid activation key. Please provide the valid Activation Key.";
|
|
64
|
+
MendAuthSevice.logger.error(
|
|
65
|
+
"Invalid activation key: wsEnvUrl is not a valid URL."
|
|
66
|
+
);
|
|
67
|
+
return;
|
|
44
68
|
}
|
|
45
69
|
baseUrl.hostname = `api-${baseUrl.hostname}`;
|
|
46
70
|
baseUrl.pathname = `/api/${apiVersion}`;
|
|
@@ -48,12 +72,17 @@ class MendAuthSevice {
|
|
|
48
72
|
MendAuthSevice.clientEmail = integratorEmail;
|
|
49
73
|
MendAuthSevice.clientKey = userKey;
|
|
50
74
|
MendAuthSevice.clientUrl = wsEnvUrl;
|
|
75
|
+
MendAuthSevice.configured = true;
|
|
51
76
|
} catch (err) {
|
|
77
|
+
MendAuthSevice.configurationError = "Something went wrong while configuring Mend. Please check the Activation Key.";
|
|
78
|
+
MendAuthSevice.logger?.error(
|
|
79
|
+
`Unexpected error while configuring Mend: ${err instanceof Error ? err.message : String(err)}`
|
|
80
|
+
);
|
|
52
81
|
MendAuthSevice.baseUrl = "";
|
|
53
82
|
MendAuthSevice.clientEmail = "";
|
|
54
83
|
MendAuthSevice.clientKey = "";
|
|
55
84
|
MendAuthSevice.clientUrl = "";
|
|
56
|
-
|
|
85
|
+
MendAuthSevice.configured = false;
|
|
57
86
|
}
|
|
58
87
|
}
|
|
59
88
|
static async login() {
|
|
@@ -134,6 +163,12 @@ class MendAuthSevice {
|
|
|
134
163
|
static getClientName() {
|
|
135
164
|
return MendAuthSevice.clientName;
|
|
136
165
|
}
|
|
166
|
+
static isConfigured() {
|
|
167
|
+
return MendAuthSevice.configured;
|
|
168
|
+
}
|
|
169
|
+
static getConfigurationError() {
|
|
170
|
+
return MendAuthSevice.configurationError;
|
|
171
|
+
}
|
|
137
172
|
}
|
|
138
173
|
|
|
139
174
|
exports.MendAuthSevice = MendAuthSevice;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.service.cjs.js","sources":["../../src/service/auth.service.ts"],"sourcesContent":["import jwt from 'jsonwebtoken';\nimport { post } from '../api';\nimport { caesarCipherDecrypt } from './auth.service.helpers';\nimport {\n JwtAuthToken,\n JwtLicenceKeyPayload,\n LoginSuccessResponseData,\n MendConfig,\n RefreshAccessTokenSuccessResponseData,\n} from './auth.services.types';\n\nenum AuthRoutes {\n LOGIN = '/login',\n REFRESH_TOKEN = '/login/accessToken',\n}\n\nexport class MendAuthSevice {\n private static authToken = '';\n private static refreshToken = '';\n private static baseUrl = '';\n private static clientEmail = '';\n private static clientKey = '';\n private static clientUrl = '';\n private static clientName = '';\n private static clientUuid = '';\n\n constructor(config: MendConfig) {\n this.getConfig(config.apiVersion, config.activationKey);\n }\n\n private getConfig(apiVersion: string, activationKey: string) {\n try {\n const licenseKey = caesarCipherDecrypt(activationKey);\n\n // Decode the license key and validate payload shape\n const decoded = jwt.decode(licenseKey);\n if (!decoded || typeof decoded !== 'object') {\n throw new Error(\n 'Invalid activation key: could not decode license payload',\n );\n }\n const jwtPayload = decoded as JwtLicenceKeyPayload;\n\n const { wsEnvUrl, integratorEmail, userKey } =\n (jwtPayload as Partial<JwtLicenceKeyPayload>) || {};\n\n // Validate required fields presence and types\n if (\n !wsEnvUrl ||\n typeof wsEnvUrl !== 'string' ||\n !integratorEmail ||\n typeof integratorEmail !== 'string' ||\n !userKey ||\n typeof userKey !== 'string'\n ) {\n throw new Error(\n 'Invalid activation key: missing required fields (wsEnvUrl, integratorEmail, userKey)',\n );\n }\n\n // Create a baseUrl from the environment url with safety checks\n let baseUrl: URL;\n try {\n baseUrl = new URL(wsEnvUrl);\n } catch {\n throw new Error('Invalid activation key: wsEnvUrl is not a valid URL');\n }\n baseUrl.hostname = `api-${baseUrl.hostname}`;\n baseUrl.pathname = `/api/${apiVersion}`;\n\n MendAuthSevice.baseUrl = baseUrl.toString();\n MendAuthSevice.clientEmail = integratorEmail;\n MendAuthSevice.clientKey = userKey;\n MendAuthSevice.clientUrl = wsEnvUrl;\n } catch (err) {\n // Log and reset to safe defaults, then rethrow so callers can decide how to handle it\n MendAuthSevice.baseUrl = '';\n MendAuthSevice.clientEmail = '';\n MendAuthSevice.clientKey = '';\n MendAuthSevice.clientUrl = '';\n throw err instanceof Error ? err : new Error(String(err));\n }\n }\n\n private static async login(): Promise<void> {\n return post<LoginSuccessResponseData>(AuthRoutes.LOGIN, {\n body: {\n email: this.clientEmail,\n userKey: this.clientKey,\n },\n })\n .then(data => {\n if (!data?.response?.refreshToken) {\n throw new Error('Login failed: missing refreshToken in response');\n }\n this.refreshToken = data.response.refreshToken;\n return Promise.resolve();\n })\n .catch(err => {\n this.refreshToken = '';\n return Promise.reject(err);\n });\n }\n\n private static async refreshAccessToken(): Promise<void> {\n return post<RefreshAccessTokenSuccessResponseData>(\n AuthRoutes.REFRESH_TOKEN,\n {\n headers: {\n 'wss-refresh-token': this.refreshToken,\n },\n },\n )\n .then(data => {\n if (!data?.response?.jwtToken) {\n throw new Error('Refresh token failed: missing jwtToken in response');\n }\n this.authToken = data.response.jwtToken;\n this.clientName = data.response.orgName ?? '';\n this.clientUuid = data.response.orgUuid ?? '';\n return Promise.resolve();\n })\n .catch(err => {\n this.authToken = '';\n this.clientName = '';\n this.clientUuid = '';\n return Promise.reject(err);\n });\n }\n\n static async connect(): Promise<void> {\n return MendAuthSevice.login()\n .then(() => MendAuthSevice.refreshAccessToken())\n .catch(err => {\n return Promise.reject(err);\n });\n }\n\n static async validateAuthToken(url: string): Promise<void> {\n if (\n [AuthRoutes.LOGIN, AuthRoutes.REFRESH_TOKEN].includes(url as AuthRoutes)\n ) {\n return Promise.resolve();\n }\n\n if (!this.authToken) {\n return this.connect();\n }\n\n const decoded = jwt.decode(this.authToken);\n if (!decoded || typeof decoded !== 'object' || !('exp' in decoded)) {\n return this.connect();\n }\n\n const token = decoded as JwtAuthToken;\n const expMs = Number(token.exp) * 1000;\n if (Number.isNaN(expMs) || expMs - Date.now() < 0) {\n return this.connect();\n }\n\n return Promise.resolve();\n }\n\n static getAuthToken(): string {\n return MendAuthSevice.authToken;\n }\n\n static getBaseUrl(): string {\n return MendAuthSevice.baseUrl;\n }\n\n static getOrganizationUuid(): string {\n return MendAuthSevice.clientUuid;\n }\n\n static getClientUrl(): string {\n return MendAuthSevice.clientUrl;\n }\n\n static getClientName(): string {\n return MendAuthSevice.clientName;\n }\n}\n"],"names":["caesarCipherDecrypt","jwt","post"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"auth.service.cjs.js","sources":["../../src/service/auth.service.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport jwt from 'jsonwebtoken';\nimport { post } from '../api';\nimport { caesarCipherDecrypt } from './auth.service.helpers';\nimport {\n JwtAuthToken,\n JwtLicenceKeyPayload,\n LoginSuccessResponseData,\n MendConfig,\n RefreshAccessTokenSuccessResponseData,\n} from './auth.services.types';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nenum AuthRoutes {\n LOGIN = '/login',\n REFRESH_TOKEN = '/login/accessToken',\n}\n\nexport class MendAuthSevice {\n private static authToken = '';\n private static refreshToken = '';\n private static baseUrl = '';\n private static clientEmail = '';\n private static clientKey = '';\n private static clientUrl = '';\n private static clientName = '';\n private static clientUuid = '';\n private static configured = false;\n private static configurationError = '';\n private static logger: LoggerService;\n\n constructor(config: MendConfig) {\n MendAuthSevice.init(config);\n }\n\n static init(config: MendConfig) {\n MendAuthSevice.logger = config.logger;\n MendAuthSevice.configure(config.apiVersion, config.activationKey);\n }\n\n private static configure(apiVersion: string, activationKey: string) {\n // Reset configuration state\n MendAuthSevice.configured = false;\n MendAuthSevice.configurationError = '';\n MendAuthSevice.baseUrl = '';\n MendAuthSevice.clientEmail = '';\n MendAuthSevice.clientKey = '';\n MendAuthSevice.clientUrl = '';\n\n // If no activation key provided, leave unconfigured but don't throw\n if (!activationKey) {\n MendAuthSevice.configurationError =\n 'Mend activation key is not configured. Please set the Activation Key in the configuration file.';\n MendAuthSevice.logger?.warn(MendAuthSevice.configurationError);\n return;\n }\n\n try {\n const licenseKey = caesarCipherDecrypt(activationKey);\n\n // Decode the license key and validate payload shape\n const decoded = jwt.decode(licenseKey);\n if (!decoded || typeof decoded !== 'object') {\n MendAuthSevice.configurationError =\n 'Invalid activation key. Please provide the valid Activation Key.';\n MendAuthSevice.logger.error(MendAuthSevice.configurationError);\n return;\n }\n const jwtPayload = decoded as JwtLicenceKeyPayload;\n\n const { wsEnvUrl, integratorEmail, userKey } =\n (jwtPayload as Partial<JwtLicenceKeyPayload>) || {};\n\n // Validate required fields presence and types\n if (\n !wsEnvUrl ||\n typeof wsEnvUrl !== 'string' ||\n !integratorEmail ||\n typeof integratorEmail !== 'string' ||\n !userKey ||\n typeof userKey !== 'string'\n ) {\n MendAuthSevice.configurationError =\n 'Invalid activation key. Please provide the valid Activation Key.';\n MendAuthSevice.logger.error(\n 'Invalid activation key: missing required fields (wsEnvUrl, integratorEmail, userKey).',\n );\n return;\n }\n\n // Create a baseUrl from the environment url with safety checks\n let baseUrl: URL;\n try {\n baseUrl = new URL(wsEnvUrl);\n } catch {\n MendAuthSevice.configurationError =\n 'Invalid activation key. Please provide the valid Activation Key.';\n MendAuthSevice.logger.error(\n 'Invalid activation key: wsEnvUrl is not a valid URL.',\n );\n return;\n }\n baseUrl.hostname = `api-${baseUrl.hostname}`;\n baseUrl.pathname = `/api/${apiVersion}`;\n\n MendAuthSevice.baseUrl = baseUrl.toString();\n MendAuthSevice.clientEmail = integratorEmail;\n MendAuthSevice.clientKey = userKey;\n MendAuthSevice.clientUrl = wsEnvUrl;\n MendAuthSevice.configured = true;\n } catch (err) {\n MendAuthSevice.configurationError =\n 'Something went wrong while configuring Mend. Please check the Activation Key.';\n MendAuthSevice.logger?.error(\n `Unexpected error while configuring Mend: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n MendAuthSevice.baseUrl = '';\n MendAuthSevice.clientEmail = '';\n MendAuthSevice.clientKey = '';\n MendAuthSevice.clientUrl = '';\n MendAuthSevice.configured = false;\n }\n }\n\n private static async login(): Promise<void> {\n return post<LoginSuccessResponseData>(AuthRoutes.LOGIN, {\n body: {\n email: this.clientEmail,\n userKey: this.clientKey,\n },\n })\n .then(data => {\n if (!data?.response?.refreshToken) {\n throw new Error('Login failed: missing refreshToken in response');\n }\n this.refreshToken = data.response.refreshToken;\n return Promise.resolve();\n })\n .catch(err => {\n this.refreshToken = '';\n return Promise.reject(err);\n });\n }\n\n private static async refreshAccessToken(): Promise<void> {\n return post<RefreshAccessTokenSuccessResponseData>(\n AuthRoutes.REFRESH_TOKEN,\n {\n headers: {\n 'wss-refresh-token': this.refreshToken,\n },\n },\n )\n .then(data => {\n if (!data?.response?.jwtToken) {\n throw new Error('Refresh token failed: missing jwtToken in response');\n }\n this.authToken = data.response.jwtToken;\n this.clientName = data.response.orgName ?? '';\n this.clientUuid = data.response.orgUuid ?? '';\n return Promise.resolve();\n })\n .catch(err => {\n this.authToken = '';\n this.clientName = '';\n this.clientUuid = '';\n return Promise.reject(err);\n });\n }\n\n static async connect(): Promise<void> {\n return MendAuthSevice.login()\n .then(() => MendAuthSevice.refreshAccessToken())\n .catch(err => {\n return Promise.reject(err);\n });\n }\n\n static async validateAuthToken(url: string): Promise<void> {\n if (\n [AuthRoutes.LOGIN, AuthRoutes.REFRESH_TOKEN].includes(url as AuthRoutes)\n ) {\n return Promise.resolve();\n }\n\n if (!this.authToken) {\n return this.connect();\n }\n\n const decoded = jwt.decode(this.authToken);\n if (!decoded || typeof decoded !== 'object' || !('exp' in decoded)) {\n return this.connect();\n }\n\n const token = decoded as JwtAuthToken;\n const expMs = Number(token.exp) * 1000;\n if (Number.isNaN(expMs) || expMs - Date.now() < 0) {\n return this.connect();\n }\n\n return Promise.resolve();\n }\n\n static getAuthToken(): string {\n return MendAuthSevice.authToken;\n }\n\n static getBaseUrl(): string {\n return MendAuthSevice.baseUrl;\n }\n\n static getOrganizationUuid(): string {\n return MendAuthSevice.clientUuid;\n }\n\n static getClientUrl(): string {\n return MendAuthSevice.clientUrl;\n }\n\n static getClientName(): string {\n return MendAuthSevice.clientName;\n }\n\n static isConfigured(): boolean {\n return MendAuthSevice.configured;\n }\n\n static getConfigurationError(): string {\n return MendAuthSevice.configurationError;\n }\n}\n"],"names":["caesarCipherDecrypt","jwt","post"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.service.helpers.cjs.js","sources":["../../src/service/auth.service.helpers.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"auth.service.helpers.cjs.js","sources":["../../src/service/auth.service.helpers.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport const caesarCipherDecrypt = (activationKey: string): string => {\n let tmp = '';\n const OFFSET = 4;\n for (let i = 0; i < activationKey.length; i++) {\n tmp += String.fromCharCode(activationKey.charCodeAt(i) - OFFSET);\n }\n\n const reversed = tmp.split('').reverse().join('');\n return Buffer.from(reversed, 'base64').toString();\n};\n"],"names":[],"mappings":";;AAeO,MAAM,mBAAA,GAAsB,CAAC,aAAA,KAAkC;AACpE,EAAA,IAAI,GAAA,GAAM,EAAA;AACV,EAAA,MAAM,MAAA,GAAS,CAAA;AACf,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,aAAA,CAAc,QAAQ,CAAA,EAAA,EAAK;AAC7C,IAAA,GAAA,IAAO,OAAO,YAAA,CAAa,aAAA,CAAc,UAAA,CAAW,CAAC,IAAI,MAAM,CAAA;AAAA,EACjE;AAEA,EAAA,MAAM,QAAA,GAAW,IAAI,KAAA,CAAM,EAAE,EAAE,OAAA,EAAQ,CAAE,KAAK,EAAE,CAAA;AAChD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,QAAA,EAAU,QAAQ,EAAE,QAAA,EAAS;AAClD;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.service.cjs.js","sources":["../../src/service/data.service.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"data.service.cjs.js","sources":["../../src/service/data.service.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { get, post } from '../api';\nimport { MendAuthSevice } from './auth.service';\nimport {\n GetOrganizationProjectRequestData,\n GetProjectStatisticsRequestData,\n GetCodeFindingsRequestData,\n GetDependenciesFindingsRequestData,\n GetContainersFindingsRequestData,\n GetOrganizationProjectSuccessResponseData,\n GetProjectStatisticsSuccessResponseData,\n GetCodeFindingSuccessResponseData,\n GetDependenciesFindingSuccessResponseData,\n GetContainersFindingSuccessResponseData,\n} from './data.service.types';\n\nexport class MendDataService extends MendAuthSevice {\n async getOrganizationProject({\n queryParams,\n }: GetOrganizationProjectRequestData): Promise<GetOrganizationProjectSuccessResponseData> {\n return get(`/orgs/${MendAuthSevice.getOrganizationUuid()}/projects`, {\n params: {\n ...queryParams,\n },\n });\n }\n\n async getProjectStatistics({\n queryParams,\n bodyParams,\n }: GetProjectStatisticsRequestData): Promise<GetProjectStatisticsSuccessResponseData> {\n return post(\n `/orgs/${MendAuthSevice.getOrganizationUuid()}/projects/summaries`,\n {\n params: {\n ...queryParams,\n },\n body: {\n ...bodyParams,\n },\n },\n );\n }\n\n async getCodeFinding({\n pathParams,\n queryParams,\n }: GetCodeFindingsRequestData): Promise<GetCodeFindingSuccessResponseData> {\n return get(`/projects/${pathParams.uuid}/code/findings`, {\n params: {\n ...queryParams,\n },\n });\n }\n\n async getDependenciesFinding({\n pathParams,\n queryParams,\n }: GetDependenciesFindingsRequestData): Promise<GetDependenciesFindingSuccessResponseData> {\n return get(`/projects/${pathParams.uuid}/dependencies/findings/security`, {\n params: {\n ...queryParams,\n },\n });\n }\n\n async getContainersFinding({\n pathParams,\n queryParams,\n }: GetContainersFindingsRequestData): Promise<GetContainersFindingSuccessResponseData> {\n return get(`/projects/${pathParams.uuid}/images/findings/security`, {\n params: {\n ...queryParams,\n },\n });\n }\n}\n"],"names":["MendAuthSevice","get","post"],"mappings":";;;;;AA8BO,MAAM,wBAAwBA,2BAAA,CAAe;AAAA,EAClD,MAAM,sBAAA,CAAuB;AAAA,IAC3B;AAAA,GACF,EAA0F;AACxF,IAAA,OAAOC,SAAA,CAAI,CAAA,MAAA,EAASD,2BAAA,CAAe,mBAAA,EAAqB,CAAA,SAAA,CAAA,EAAa;AAAA,MACnE,MAAA,EAAQ;AAAA,QACN,GAAG;AAAA;AACL,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,oBAAA,CAAqB;AAAA,IACzB,WAAA;AAAA,IACA;AAAA,GACF,EAAsF;AACpF,IAAA,OAAOE,UAAA;AAAA,MACL,CAAA,MAAA,EAASF,2BAAA,CAAe,mBAAA,EAAqB,CAAA,mBAAA,CAAA;AAAA,MAC7C;AAAA,QACE,MAAA,EAAQ;AAAA,UACN,GAAG;AAAA,SACL;AAAA,QACA,IAAA,EAAM;AAAA,UACJ,GAAG;AAAA;AACL;AACF,KACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAA,CAAe;AAAA,IACnB,UAAA;AAAA,IACA;AAAA,GACF,EAA2E;AACzE,IAAA,OAAOC,SAAA,CAAI,CAAA,UAAA,EAAa,UAAA,CAAW,IAAI,CAAA,cAAA,CAAA,EAAkB;AAAA,MACvD,MAAA,EAAQ;AAAA,QACN,GAAG;AAAA;AACL,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,sBAAA,CAAuB;AAAA,IAC3B,UAAA;AAAA,IACA;AAAA,GACF,EAA2F;AACzF,IAAA,OAAOA,SAAA,CAAI,CAAA,UAAA,EAAa,UAAA,CAAW,IAAI,CAAA,+BAAA,CAAA,EAAmC;AAAA,MACxE,MAAA,EAAQ;AAAA,QACN,GAAG;AAAA;AACL,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,oBAAA,CAAqB;AAAA,IACzB,UAAA;AAAA,IACA;AAAA,GACF,EAAuF;AACrF,IAAA,OAAOA,SAAA,CAAI,CAAA,UAAA,EAAa,UAAA,CAAW,IAAI,CAAA,yBAAA,CAAA,EAA6B;AAAA,MAClE,MAAA,EAAQ;AAAA,QACN,GAAG;AAAA;AACL,KACD,CAAA;AAAA,EACH;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.service.helpers.cjs.js","sources":["../../src/service/data.service.helpers.ts"],"sourcesContent":["import { Entity } from '@backstage/catalog-model';\nimport { match } from 'path-to-regexp';\nimport type { QueryParams } from '../api';\nimport {\n ProjectStatisticsSuccessResponseData,\n EntityURL,\n OrganizationProjectSuccessResponseData,\n PaginationQueryParams,\n Project,\n CodeFindingSuccessResponseData,\n DependenciesFindingSuccessResponseData,\n ContainersFindingSuccessResponseData,\n Finding,\n StatisticsEngine,\n StatisticsName,\n} from './data.service.types';\nimport { AZURE_HOST_NAME } from '../constants';\n\nenum FINDING_TYPE {\n DEPENDENCIES = 'ALERTS',\n CODE = 'SAST_VULNERABILITIES_BY_SEVERITY',\n CONTAINERS = 'IMG_SECURITY',\n LAST_SCAN = 'LAST_SCAN',\n}\n\ntype OverviewData = {\n projectList: Project[];\n};\n\nexport const dataProjectParser = (\n projectStatistics: Array<\n ProjectStatisticsSuccessResponseData & { entity: EntityURL }\n >,\n organizationProjects: OrganizationProjectSuccessResponseData[],\n) => {\n const organizationData = organizationProjects.reduce((prev, next) => {\n prev[next.uuid] = next;\n return prev;\n }, {} as { [key: string]: OrganizationProjectSuccessResponseData });\n\n const projectData = projectStatistics.reduce(\n (\n prev: OverviewData,\n next: ProjectStatisticsSuccessResponseData & { entity: EntityURL },\n ) => {\n const dependenciesCritical =\n next.statistics[FINDING_TYPE.DEPENDENCIES]\n .criticalSeverityVulnerabilities;\n const dependenciesHigh =\n next.statistics[FINDING_TYPE.DEPENDENCIES].highSeverityVulnerabilities;\n const dependenciesMedium =\n next.statistics[FINDING_TYPE.DEPENDENCIES]\n .mediumSeverityVulnerabilities;\n const dependenciesLow =\n next.statistics[FINDING_TYPE.DEPENDENCIES].lowSeverityVulnerabilities;\n const dependeciesTotal =\n dependenciesCritical +\n dependenciesHigh +\n dependenciesMedium +\n dependenciesLow;\n\n const codeHigh =\n next.statistics[FINDING_TYPE.CODE].sastHighVulnerabilities;\n const codeMedium =\n next.statistics[FINDING_TYPE.CODE].sastMediumVulnerabilities;\n const codeLow = next.statistics[FINDING_TYPE.CODE].sastLowVulnerabilities;\n const codeTotal = codeHigh + codeMedium + codeLow;\n\n const containersCritical =\n next.statistics[FINDING_TYPE.CONTAINERS].imgCriticalVulnerabilities;\n const containersHigh =\n next.statistics[FINDING_TYPE.CONTAINERS].imgHighVulnerabilities;\n const containersMedium =\n next.statistics[FINDING_TYPE.CONTAINERS].imgMediumVulnerabilities;\n const containersLow =\n next.statistics[FINDING_TYPE.CONTAINERS].imgLowVulnerabilities;\n const containersTotal =\n containersCritical + containersHigh + containersMedium + containersLow;\n\n const criticalTotal = dependenciesCritical + containersCritical;\n const highTotal = dependenciesHigh + codeHigh + containersHigh;\n const mediumTotal = dependenciesMedium + codeMedium + containersMedium;\n const lowTotal = dependenciesLow + codeLow + containersLow;\n const total = dependeciesTotal + codeTotal + containersTotal;\n\n const statistics = {\n [StatisticsEngine.DEPENDENCIES]: {\n critical: dependenciesCritical,\n high: dependenciesHigh,\n medium: dependenciesMedium,\n low: dependenciesLow,\n total: dependeciesTotal,\n },\n [StatisticsEngine.CODE]: {\n critical: null,\n high: codeHigh,\n medium: codeMedium,\n low: codeLow,\n total: codeTotal,\n },\n [StatisticsEngine.CONTAINERS]: {\n critical: containersCritical,\n high: containersHigh,\n medium: containersMedium,\n low: containersLow,\n total: containersTotal,\n },\n critical: criticalTotal,\n high: highTotal,\n medium: mediumTotal,\n low: lowTotal,\n total: total,\n };\n\n const project = {\n statistics,\n uuid: next.uuid,\n name: next.name,\n path: next.path,\n entity: next.entity,\n applicationName: organizationData[next.uuid].applicationName,\n applicationUuid: next.applicationUuid,\n lastScan: next.statistics[FINDING_TYPE.LAST_SCAN].lastScanTime,\n languages: next.statistics?.LIBRARY_TYPE_HISTOGRAM\n ? Object.entries(next.statistics.LIBRARY_TYPE_HISTOGRAM).sort(\n (a, b) => b[1] - a[1],\n )\n : ([] as [string, number][]),\n };\n\n prev.projectList.unshift(project);\n return prev;\n },\n {\n projectList: [],\n },\n );\n\n projectData.projectList.sort(\n (a, b) => b.statistics.critical - a.statistics.critical,\n );\n\n return projectData;\n};\n\nexport const parseEntityURL = (entityUrl?: string) => {\n try {\n if (!entityUrl) {\n return null;\n }\n\n const matches = entityUrl.match(\n /https?:\\/\\/[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z]{2,}(:[0-9]{1,5})?(\\/.*)?/g,\n );\n\n if (!matches) {\n return null;\n }\n const url = new URL(matches[0]);\n const hostname = url.host.toLowerCase();\n let matcher = match('/:org/:repo', { end: false });\n\n if (hostname === AZURE_HOST_NAME) {\n matcher = match('/:org/:project/_git/:repo', { end: false });\n }\n const extractedContent = matcher(url.pathname);\n if (extractedContent) {\n return { ...extractedContent, host: hostname };\n }\n return null;\n } catch (error) {\n return null;\n }\n};\n\nexport const dataMatcher = (\n entities: Entity[],\n projects: ProjectStatisticsSuccessResponseData[],\n) => {\n const projectSourceURL = getSourceURLWiseProject(projects);\n return entities.reduce(\n (\n prev: Array<\n ProjectStatisticsSuccessResponseData & {\n entity: EntityURL;\n }\n >,\n next: Entity,\n ) => {\n const entityURL = parseEntityURL(\n next?.metadata?.annotations?.['backstage.io/source-location'],\n );\n\n if (!entityURL) {\n return prev;\n }\n\n // NOTE: Find project based on Github URL\n const relatedProjects =\n projectSourceURL[`${entityURL?.host}${entityURL?.path}`]?.projectObjs;\n\n if (!relatedProjects) {\n return prev;\n }\n\n const entity = {\n path: entityURL.path,\n params: entityURL.params,\n namespace: next.metadata.namespace,\n kind: 'component',\n source: 'catalog',\n };\n\n relatedProjects.forEach(project => prev.push({ ...project, entity }));\n\n return prev;\n },\n [],\n );\n};\n\n/**\n * Extracts the source URL details from each project and returns a dictionary\n * where each key is a combination of the URL's host and pathname,\n * and the value is an object containing the original project and the parsed source URL data.\n *\n * @param projects Array of ProjectStatisticsSuccessResponseData\n * @returns A dictionary object with keys as `${host}${pathname}` strings extracted from sourceUrl and values as:\n * {\n * projectObjs: ProjectStatisticsSuccessResponseData[];\n sourceUrl: string | null;\n host: string | null;\n pathname: string | null;\n * }\n */\nexport function getSourceURLWiseProject(\n projects: ProjectStatisticsSuccessResponseData[],\n) {\n return projects.reduce(\n (acc, project) => {\n const projectTags = project.tags as Array<{ key: string; value: string }>;\n const sourceUrlTag = projectTags?.find(tag => tag.key === 'sourceUrl');\n let host = null;\n let pathname = null;\n let sourceUrl = null;\n\n if (sourceUrlTag && typeof sourceUrlTag.value === 'string') {\n sourceUrl = sourceUrlTag.value;\n const urlString = sourceUrl.startsWith('http')\n ? sourceUrl\n : `https://${sourceUrl}`;\n\n try {\n const urlObj = new URL(urlString);\n host = urlObj.host.toLocaleLowerCase();\n // Remove leading/trailing slashes and split\n pathname = urlObj.pathname;\n } catch (e) {\n // fallback: leave as nulls\n }\n }\n\n if (acc[`${host}${pathname}`]) {\n acc[`${host}${pathname}`].projectObjs.push(project);\n } else {\n acc[`${host}${pathname}`] = {\n projectObjs: [project],\n sourceUrl,\n host,\n pathname,\n };\n }\n return acc;\n },\n {} as Record<\n string,\n {\n projectObjs: ProjectStatisticsSuccessResponseData[];\n sourceUrl: string | null;\n host: string | null;\n pathname: string | null;\n }\n >,\n );\n}\n\nconst getIssueStatus = (\n engine: StatisticsEngine,\n finding:\n | CodeFindingSuccessResponseData\n | DependenciesFindingSuccessResponseData\n | ContainersFindingSuccessResponseData,\n): string => {\n if (engine === StatisticsEngine.CODE) {\n if ((finding as CodeFindingSuccessResponseData)?.suppressed)\n return 'suppressed';\n if (\n (finding as CodeFindingSuccessResponseData)?.almIssues?.jiraPlatform\n ?.issueStatus\n )\n return 'created';\n if ((finding as CodeFindingSuccessResponseData)?.reviewed)\n return 'reviewed';\n }\n\n if (engine === StatisticsEngine.DEPENDENCIES) {\n // NOTE: Available status: IGNORED and ACTIVE\n // ACTIVE means unreviewed\n // IGNORED means suppressed, comment fields are available to this status\n if (\n (finding as DependenciesFindingSuccessResponseData)?.findingInfo\n ?.status === 'IGNORED'\n )\n return 'suppressed';\n }\n\n return 'unreviewed';\n};\n\nexport const dataFindingParser = (\n code: CodeFindingSuccessResponseData[] = [],\n dependencies: DependenciesFindingSuccessResponseData[] = [],\n containers: ContainersFindingSuccessResponseData[] = [],\n projectName: string = '',\n) => {\n let codeFindings: Finding[] = [];\n let dependenciesFindings: Finding[] = [];\n let containersFindings: Finding[] = [];\n\n if (code.length) {\n codeFindings = code.map(finding => {\n return {\n kind: StatisticsEngine.CODE,\n level: finding.severity.toLowerCase() as StatisticsName,\n name: finding.type.cwe.title,\n origin: `${finding.sharedStep.file}:${finding.sharedStep.line}`,\n time: finding?.createdTime,\n projectId: finding.projectId,\n projectName: projectName,\n issue: {\n issueStatus: finding.almIssues.jiraPlatform.issueStatus,\n reporter: finding.almIssues.jiraPlatform.createdByName,\n creationDate: finding.almIssues.jiraPlatform.createdTime,\n ticketName: finding.almIssues.jiraPlatform.issueKey,\n link: `${finding.almIssues.jiraPlatform.publicLink}/browse/${finding.almIssues.jiraPlatform.issueKey}`,\n status: getIssueStatus(StatisticsEngine.CODE, finding),\n },\n };\n });\n }\n\n if (dependencies.length) {\n dependenciesFindings = dependencies.map(finding => {\n return {\n kind: StatisticsEngine.DEPENDENCIES,\n level: finding.vulnerability.severity.toLowerCase() as StatisticsName,\n name: finding.vulnerability.name,\n origin: finding.component.name,\n time: finding.vulnerability.modifiedDate,\n projectId: finding.project.uuid,\n projectName: projectName,\n issue: {\n issueStatus: '',\n reporter: '',\n creationDate: '',\n ticketName: '',\n link: '',\n status: getIssueStatus(StatisticsEngine.DEPENDENCIES, finding),\n },\n };\n });\n }\n\n if (containers.length) {\n containersFindings = containers.map(finding => {\n return {\n kind: StatisticsEngine.CONTAINERS,\n level: finding.severity.toLowerCase() as StatisticsName,\n name: finding.vulnerabilityId,\n origin: finding.packageName,\n time: finding.detectionDate,\n projectId: finding.projectUuid,\n projectName: projectName,\n issue: {\n issueStatus: '',\n reporter: '',\n creationDate: '',\n ticketName: '',\n link: '',\n status: getIssueStatus(StatisticsEngine.CONTAINERS, finding), // NOTE: Currently, issue for finding in containers no exist.\n },\n };\n });\n }\n\n const order: { [k: string]: number } = {\n critical: 1,\n high: 2,\n medium: 3,\n low: 4,\n };\n\n return [...codeFindings, ...dependenciesFindings, ...containersFindings].sort(\n (a, b) => {\n return order[a.level] - order[b.level];\n },\n );\n};\n\nconst parseQueryString = (href = '?'): QueryParams => {\n const [, queryString] = href.split('?');\n\n const queryParams: QueryParams = {};\n new URLSearchParams(queryString).forEach((val, key) => {\n queryParams[key] = val;\n });\n\n return queryParams;\n};\n\nexport const fetchQueryPagination = async <T>(cb: Function) => {\n const defaultQueryParams = { limit: '10000', cursor: '0' };\n const collection: T[] = [];\n\n const fetchLoop = async (queryParams: PaginationQueryParams) => {\n const result = await cb({ queryParams });\n\n collection.push(...result.response);\n\n const nextQuery = result.additionalData?.paging?.next;\n\n if (nextQuery) {\n const newQueryParams = parseQueryString(nextQuery);\n await fetchLoop(newQueryParams);\n }\n };\n\n await fetchLoop(defaultQueryParams);\n\n return collection;\n};\n"],"names":["StatisticsEngine","match","AZURE_HOST_NAME"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data.service.helpers.cjs.js","sources":["../../src/service/data.service.helpers.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from '@backstage/catalog-model';\nimport { match } from 'path-to-regexp';\nimport type { QueryParams } from '../api';\nimport {\n ProjectStatisticsSuccessResponseData,\n EntityURL,\n OrganizationProjectSuccessResponseData,\n PaginationQueryParams,\n Project,\n CodeFindingSuccessResponseData,\n DependenciesFindingSuccessResponseData,\n ContainersFindingSuccessResponseData,\n Finding,\n StatisticsEngine,\n StatisticsName,\n} from './data.service.types';\nimport { AZURE_HOST_NAME } from '../constants';\n\nenum FINDING_TYPE {\n DEPENDENCIES = 'ALERTS',\n CODE = 'SAST_VULNERABILITIES_BY_SEVERITY',\n CONTAINERS = 'IMG_SECURITY',\n LAST_SCAN = 'LAST_SCAN',\n}\n\ntype OverviewData = {\n projectList: Project[];\n};\n\nexport const dataProjectParser = (\n projectStatistics: Array<\n ProjectStatisticsSuccessResponseData & { entity: EntityURL }\n >,\n organizationProjects: OrganizationProjectSuccessResponseData[],\n) => {\n const organizationData = organizationProjects.reduce((prev, next) => {\n prev[next.uuid] = next;\n return prev;\n }, {} as { [key: string]: OrganizationProjectSuccessResponseData });\n\n const projectData = projectStatistics.reduce(\n (\n prev: OverviewData,\n next: ProjectStatisticsSuccessResponseData & { entity: EntityURL },\n ) => {\n const dependenciesCritical =\n next.statistics[FINDING_TYPE.DEPENDENCIES]\n .criticalSeverityVulnerabilities;\n const dependenciesHigh =\n next.statistics[FINDING_TYPE.DEPENDENCIES].highSeverityVulnerabilities;\n const dependenciesMedium =\n next.statistics[FINDING_TYPE.DEPENDENCIES]\n .mediumSeverityVulnerabilities;\n const dependenciesLow =\n next.statistics[FINDING_TYPE.DEPENDENCIES].lowSeverityVulnerabilities;\n const dependeciesTotal =\n dependenciesCritical +\n dependenciesHigh +\n dependenciesMedium +\n dependenciesLow;\n\n const codeHigh =\n next.statistics[FINDING_TYPE.CODE].sastHighVulnerabilities;\n const codeMedium =\n next.statistics[FINDING_TYPE.CODE].sastMediumVulnerabilities;\n const codeLow = next.statistics[FINDING_TYPE.CODE].sastLowVulnerabilities;\n const codeTotal = codeHigh + codeMedium + codeLow;\n\n const containersCritical =\n next.statistics[FINDING_TYPE.CONTAINERS].imgCriticalVulnerabilities;\n const containersHigh =\n next.statistics[FINDING_TYPE.CONTAINERS].imgHighVulnerabilities;\n const containersMedium =\n next.statistics[FINDING_TYPE.CONTAINERS].imgMediumVulnerabilities;\n const containersLow =\n next.statistics[FINDING_TYPE.CONTAINERS].imgLowVulnerabilities;\n const containersTotal =\n containersCritical + containersHigh + containersMedium + containersLow;\n\n const criticalTotal = dependenciesCritical + containersCritical;\n const highTotal = dependenciesHigh + codeHigh + containersHigh;\n const mediumTotal = dependenciesMedium + codeMedium + containersMedium;\n const lowTotal = dependenciesLow + codeLow + containersLow;\n const total = dependeciesTotal + codeTotal + containersTotal;\n\n const statistics = {\n [StatisticsEngine.DEPENDENCIES]: {\n critical: dependenciesCritical,\n high: dependenciesHigh,\n medium: dependenciesMedium,\n low: dependenciesLow,\n total: dependeciesTotal,\n },\n [StatisticsEngine.CODE]: {\n critical: null,\n high: codeHigh,\n medium: codeMedium,\n low: codeLow,\n total: codeTotal,\n },\n [StatisticsEngine.CONTAINERS]: {\n critical: containersCritical,\n high: containersHigh,\n medium: containersMedium,\n low: containersLow,\n total: containersTotal,\n },\n critical: criticalTotal,\n high: highTotal,\n medium: mediumTotal,\n low: lowTotal,\n total: total,\n };\n\n const project = {\n statistics,\n uuid: next.uuid,\n name: next.name,\n path: next.path,\n entity: next.entity,\n applicationName: organizationData[next.uuid].applicationName,\n applicationUuid: next.applicationUuid,\n lastScan: next.statistics[FINDING_TYPE.LAST_SCAN].lastScanTime,\n languages: next.statistics?.LIBRARY_TYPE_HISTOGRAM\n ? Object.entries(next.statistics.LIBRARY_TYPE_HISTOGRAM).sort(\n (a, b) => b[1] - a[1],\n )\n : ([] as [string, number][]),\n };\n\n prev.projectList.unshift(project);\n return prev;\n },\n {\n projectList: [],\n },\n );\n\n projectData.projectList.sort(\n (a, b) => b.statistics.critical - a.statistics.critical,\n );\n\n return projectData;\n};\n\nexport const parseEntityURL = (entityUrl?: string) => {\n try {\n if (!entityUrl) {\n return null;\n }\n\n const matches = entityUrl.match(\n /https?:\\/\\/[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z]{2,}(:[0-9]{1,5})?(\\/.*)?/g,\n );\n\n if (!matches) {\n return null;\n }\n const url = new URL(matches[0]);\n const hostname = url.host.toLowerCase();\n let matcher = match('/:org/:repo', { end: false });\n\n if (hostname === AZURE_HOST_NAME) {\n matcher = match('/:org/:project/_git/:repo', { end: false });\n }\n const extractedContent = matcher(url.pathname);\n if (extractedContent) {\n return { ...extractedContent, host: hostname };\n }\n return null;\n } catch (error) {\n return null;\n }\n};\n\nexport const dataMatcher = (\n entities: Entity[],\n projects: ProjectStatisticsSuccessResponseData[],\n) => {\n const projectSourceURL = getSourceURLWiseProject(projects);\n return entities.reduce(\n (\n prev: Array<\n ProjectStatisticsSuccessResponseData & {\n entity: EntityURL;\n }\n >,\n next: Entity,\n ) => {\n const entityURL = parseEntityURL(\n next?.metadata?.annotations?.['backstage.io/source-location'],\n );\n\n if (!entityURL) {\n return prev;\n }\n\n // NOTE: Find project based on Github URL\n const relatedProjects =\n projectSourceURL[`${entityURL?.host}${entityURL?.path}`]?.projectObjs;\n\n if (!relatedProjects) {\n return prev;\n }\n\n const entity = {\n path: entityURL.path,\n params: entityURL.params,\n namespace: next.metadata.namespace,\n kind: 'component',\n source: 'catalog',\n };\n\n relatedProjects.forEach(project => prev.push({ ...project, entity }));\n\n return prev;\n },\n [],\n );\n};\n\n/**\n * Extracts the source URL details from each project and returns a dictionary\n * where each key is a combination of the URL's host and pathname,\n * and the value is an object containing the original project and the parsed source URL data.\n *\n * @param projects Array of ProjectStatisticsSuccessResponseData\n * @returns A dictionary object with keys as `${host}${pathname}` strings extracted from sourceUrl and values as:\n * {\n * projectObjs: ProjectStatisticsSuccessResponseData[];\n sourceUrl: string | null;\n host: string | null;\n pathname: string | null;\n * }\n */\nexport function getSourceURLWiseProject(\n projects: ProjectStatisticsSuccessResponseData[],\n) {\n return projects.reduce(\n (acc, project) => {\n const projectTags = project.tags as Array<{ key: string; value: string }>;\n const sourceUrlTag = projectTags?.find(tag => tag.key === 'sourceUrl');\n let host = null;\n let pathname = null;\n let sourceUrl = null;\n\n if (sourceUrlTag && typeof sourceUrlTag.value === 'string') {\n sourceUrl = sourceUrlTag.value;\n const urlString = sourceUrl.startsWith('http')\n ? sourceUrl\n : `https://${sourceUrl}`;\n\n try {\n const urlObj = new URL(urlString);\n host = urlObj.host.toLocaleLowerCase();\n // Remove leading/trailing slashes and split\n pathname = urlObj.pathname;\n } catch (e) {\n // fallback: leave as nulls\n }\n }\n\n if (acc[`${host}${pathname}`]) {\n acc[`${host}${pathname}`].projectObjs.push(project);\n } else {\n acc[`${host}${pathname}`] = {\n projectObjs: [project],\n sourceUrl,\n host,\n pathname,\n };\n }\n return acc;\n },\n {} as Record<\n string,\n {\n projectObjs: ProjectStatisticsSuccessResponseData[];\n sourceUrl: string | null;\n host: string | null;\n pathname: string | null;\n }\n >,\n );\n}\n\nconst getIssueStatus = (\n engine: StatisticsEngine,\n finding:\n | CodeFindingSuccessResponseData\n | DependenciesFindingSuccessResponseData\n | ContainersFindingSuccessResponseData,\n): string => {\n if (engine === StatisticsEngine.CODE) {\n if ((finding as CodeFindingSuccessResponseData)?.suppressed)\n return 'suppressed';\n if (\n (finding as CodeFindingSuccessResponseData)?.almIssues?.jiraPlatform\n ?.issueStatus\n )\n return 'created';\n if ((finding as CodeFindingSuccessResponseData)?.reviewed)\n return 'reviewed';\n }\n\n if (engine === StatisticsEngine.DEPENDENCIES) {\n // NOTE: Available status: IGNORED and ACTIVE\n // ACTIVE means unreviewed\n // IGNORED means suppressed, comment fields are available to this status\n if (\n (finding as DependenciesFindingSuccessResponseData)?.findingInfo\n ?.status === 'IGNORED'\n )\n return 'suppressed';\n }\n\n return 'unreviewed';\n};\n\nexport const dataFindingParser = (\n code: CodeFindingSuccessResponseData[] = [],\n dependencies: DependenciesFindingSuccessResponseData[] = [],\n containers: ContainersFindingSuccessResponseData[] = [],\n projectName: string = '',\n) => {\n let codeFindings: Finding[] = [];\n let dependenciesFindings: Finding[] = [];\n let containersFindings: Finding[] = [];\n\n if (code.length) {\n codeFindings = code.map(finding => {\n return {\n kind: StatisticsEngine.CODE,\n level: finding.severity.toLowerCase() as StatisticsName,\n name: finding.type.cwe.title,\n origin: `${finding.sharedStep.file}:${finding.sharedStep.line}`,\n time: finding?.createdTime,\n projectId: finding.projectId,\n projectName: projectName,\n issue: {\n issueStatus: finding.almIssues.jiraPlatform.issueStatus,\n reporter: finding.almIssues.jiraPlatform.createdByName,\n creationDate: finding.almIssues.jiraPlatform.createdTime,\n ticketName: finding.almIssues.jiraPlatform.issueKey,\n link: `${finding.almIssues.jiraPlatform.publicLink}/browse/${finding.almIssues.jiraPlatform.issueKey}`,\n status: getIssueStatus(StatisticsEngine.CODE, finding),\n },\n };\n });\n }\n\n if (dependencies.length) {\n dependenciesFindings = dependencies.map(finding => {\n return {\n kind: StatisticsEngine.DEPENDENCIES,\n level: finding.vulnerability.severity.toLowerCase() as StatisticsName,\n name: finding.vulnerability.name,\n origin: finding.component.name,\n time: finding.vulnerability.modifiedDate,\n projectId: finding.project.uuid,\n projectName: projectName,\n issue: {\n issueStatus: '',\n reporter: '',\n creationDate: '',\n ticketName: '',\n link: '',\n status: getIssueStatus(StatisticsEngine.DEPENDENCIES, finding),\n },\n };\n });\n }\n\n if (containers.length) {\n containersFindings = containers.map(finding => {\n return {\n kind: StatisticsEngine.CONTAINERS,\n level: finding.severity.toLowerCase() as StatisticsName,\n name: finding.vulnerabilityId,\n origin: finding.packageName,\n time: finding.detectionDate,\n projectId: finding.projectUuid,\n projectName: projectName,\n issue: {\n issueStatus: '',\n reporter: '',\n creationDate: '',\n ticketName: '',\n link: '',\n status: getIssueStatus(StatisticsEngine.CONTAINERS, finding), // NOTE: Currently, issue for finding in containers no exist.\n },\n };\n });\n }\n\n const order: { [k: string]: number } = {\n critical: 1,\n high: 2,\n medium: 3,\n low: 4,\n };\n\n return [...codeFindings, ...dependenciesFindings, ...containersFindings].sort(\n (a, b) => {\n return order[a.level] - order[b.level];\n },\n );\n};\n\nconst parseQueryString = (href = '?'): QueryParams => {\n const [, queryString] = href.split('?');\n\n const queryParams: QueryParams = {};\n new URLSearchParams(queryString).forEach((val, key) => {\n queryParams[key] = val;\n });\n\n return queryParams;\n};\n\nexport const fetchQueryPagination = async <T>(cb: Function) => {\n const defaultQueryParams = { limit: '10000', cursor: '0' };\n const collection: T[] = [];\n\n const fetchLoop = async (queryParams: PaginationQueryParams) => {\n const result = await cb({ queryParams });\n\n collection.push(...result.response);\n\n const nextQuery = result.additionalData?.paging?.next;\n\n if (nextQuery) {\n const newQueryParams = parseQueryString(nextQuery);\n await fetchLoop(newQueryParams);\n }\n };\n\n await fetchLoop(defaultQueryParams);\n\n return collection;\n};\n"],"names":["StatisticsEngine","match","AZURE_HOST_NAME"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.service.types.cjs.js","sources":["../../src/service/data.service.types.ts"],"sourcesContent":["export type PaginationQueryParams = {\n cursor?: string;\n limit?: string;\n};\n\ntype PaginationSuccessResponseData = {\n additionalData: {\n totalItems: number;\n paging: {\n next?: string;\n };\n };\n};\n\ntype BodyParams = {\n projectUuids?: string[];\n applicationUuid?: string[];\n};\n\ntype PathParams = {\n uuid: string;\n};\n\nexport type GetOrganizationProjectRequestData = {\n queryParams?: PaginationQueryParams;\n};\n\nexport type OrganizationProjectSuccessResponseData = {\n uuid: string;\n name: string;\n path: string;\n applicationName: string;\n applicationUuid: string;\n};\n\nexport type GetOrganizationProjectSuccessResponseData = {\n supportToken: string;\n response: OrganizationProjectSuccessResponseData[];\n} & PaginationSuccessResponseData;\n\nexport type GetProjectStatisticsRequestData = {\n queryParams?: PaginationQueryParams;\n bodyParams?: BodyParams;\n};\n\nexport type ProjectStatisticsSuccessResponseData = {\n uuid: string;\n name: string;\n path: string;\n applicationUuid: string;\n creationDate: string;\n tags: [];\n labels: [];\n statistics: {\n UNIFIED_VULNERABILITIES: {\n unifiedCriticalVulnerabilities: number;\n unifiedHighVulnerabilities: number;\n unifiedMediumVulnerabilities: number;\n unifiedLowVulnerabilities: number;\n unifiedVulnerabilities: number;\n };\n VULNERABILITY_EFFECTIVENESS: {};\n LIBRARY_TYPE_HISTOGRAM: Record<string, number>;\n IMG_USAGE: {};\n POLICY_VIOLATION_LIBRARIES: {\n policyViolatingLibraries: number;\n };\n SAST_VULNERABILITIES_BY_TYPE: Record<string, number>;\n GENERAL: {\n totalLibraries: number;\n };\n LLM_SECURITY: {\n llmTotalLines: number;\n };\n IMG_SECURITY: {\n imgCriticalVulnerabilities: number;\n imgMaxRiskScore: number;\n imgMediumVulnerabilities: number;\n imgLowVulnerabilities: number;\n imgSecretMediumVulnerabilities: number;\n imgUnknownVulnerabilities: number;\n imgSecretHighVulnerabilities: number;\n imgTotalVulnerabilities: number;\n imgHighVulnerabilities: number;\n imgSecretCriticalVulnerabilities: number;\n imgSecretLowVulnerabilities: number;\n };\n ALERTS: {\n criticalSeverityVulnerabilities: number;\n highSeverityVulnerabilities: number;\n vulnerableLibraries: number;\n mediumSeverityVulnerabilities: number;\n lowSeverityVulnerabilities: number;\n };\n OUTDATED_LIBRARIES: {\n outdatedLibraries: number;\n };\n POLICY_VIOLATIONS: {};\n SAST_SCAN: {\n sastTotalLines: number;\n sastTestedFiles: number;\n sastTotalFiles: number;\n sastTestedLines: number;\n sastTotalMended: number;\n sastTotalRemediations: number;\n };\n VULNERABILITY_SEVERITY_LIBRARIES: {\n lowSeverityLibraries: number;\n highSeverityLibraries: number;\n mediumSeverityLibraries: number;\n criticalSeverityLibraries: number;\n };\n LICENSE_RISK: {\n highRiskLicenses: number;\n mediumRiskLicenses: number;\n lowRiskLicenses: number;\n };\n IAC_SECURITY: {\n iacCriticalMisconfigurations: number;\n iacHighMisconfigurations: number;\n iacTotalMisconfigurations: number;\n iacLowMisconfigurations: number;\n iacMediumMisconfigurations: number;\n };\n SCA_SECURITY: {};\n LICENSE_HISTOGRAM: Record<string, number>;\n SAST_VULNERABILITIES_BY_SEVERITY: {\n sastVulnerabilities: number;\n sastHighVulnerabilities: number;\n sastMediumVulnerabilities: number;\n sastLowVulnerabilities: number;\n };\n LAST_SCAN: {\n lastScanTime: number;\n lastScaScanTime: number;\n lastImgScanTime: number;\n lastSastScanTime: number;\n };\n };\n};\n\nexport type GetProjectStatisticsSuccessResponseData = {\n supportToken: string;\n response: ProjectStatisticsSuccessResponseData[];\n} & PaginationSuccessResponseData;\n\nexport type EntityURL = {\n path: string;\n params: {\n org?: string;\n repo?: string;\n };\n namespace?: string;\n kind: string;\n source: string;\n};\n\nexport enum StatisticsName {\n CRITICAL = 'critical',\n HIGH = 'high',\n MEDIUM = 'medium',\n LOW = 'low',\n TOTAL = 'total',\n}\n\nexport enum StatisticsEngine {\n DEPENDENCIES = 'dependencies',\n CODE = 'code',\n CONTAINERS = 'containers',\n}\n\ntype StatisticsBase = {\n [StatisticsName.CRITICAL]: number;\n [StatisticsName.HIGH]: number;\n [StatisticsName.MEDIUM]: number;\n [StatisticsName.LOW]: number;\n [StatisticsName.TOTAL]: number;\n};\n\nexport type Statistics = {\n [StatisticsEngine.DEPENDENCIES]: StatisticsBase;\n [StatisticsEngine.CODE]: Omit<StatisticsBase, StatisticsName.CRITICAL> & {\n [StatisticsName.CRITICAL]: null;\n };\n [StatisticsEngine.CONTAINERS]: StatisticsBase;\n} & StatisticsBase;\n\nexport type Project = {\n statistics: Statistics;\n uuid: string;\n name: string;\n path: string;\n applicationName: string;\n applicationUuid: string;\n lastScan: number;\n languages: Array<[string, number]>;\n entity: EntityURL;\n};\n\n// Code Finding API Data\ntype CodeFindingDataFlowSuccessResponseData = {\n id: string;\n sink: string;\n sinkKind: string;\n sinkFile: string;\n sinkSnippet: string;\n sinkLine: number;\n inputSource: string;\n inputKind: string;\n inputFlow: [\n {\n name: string;\n kind: string;\n file: string;\n snippet: string;\n line: number;\n startLine: number;\n endLine: number;\n },\n ];\n functionCalls: [\n {\n name: string;\n kind: string;\n file: string;\n snippet: string;\n line: number;\n startLine: number;\n endLine: number;\n },\n ];\n filter: {\n isFiltered: boolean;\n filterTypes: unknown[];\n };\n isNew: boolean;\n rating: number;\n confidenceRating: number;\n ageRating: number;\n};\n\nexport type CodeFindingSuccessResponseData = {\n id: string;\n scanId: string;\n snapshotId: string;\n projectId: string;\n appId: string;\n type: {\n id: number;\n name: string;\n engineId: number;\n language: string;\n sarif: string;\n sarifLevel: string;\n order: number;\n severity: StatisticsName;\n severityRating: number;\n description: string;\n recommendations: [string];\n references: [string];\n cwe: {\n id: string;\n title: string;\n url: string;\n };\n pcidss: {\n section: string;\n title: string;\n };\n nist: {\n control: string;\n priority: string;\n title: string;\n url: string;\n };\n hipaa: {\n control: string;\n title: string;\n };\n hitrust: {\n control: string;\n title: string;\n };\n owasp: {\n index: string;\n title: string;\n url: string;\n };\n owasp2021: {\n index: string;\n title: string;\n url: string;\n };\n capec: {\n id: string;\n title: string;\n url: string;\n };\n sansTop25: {\n rank: number;\n title: string;\n };\n };\n description: string;\n createdTime: string;\n isNew: boolean;\n severity: StatisticsName;\n baseline: boolean;\n hasRemediation: boolean;\n suppressed: boolean;\n suppressedBy: string;\n suppressionTime: string;\n suppressionMessage: string;\n reviewed: boolean;\n IssueStatus: number;\n sharedStep: {\n name: string;\n kind: string;\n file: string;\n snippet: string;\n line: number;\n startLine: number;\n endLine: number;\n lineBlame: {\n commitId: string;\n file: string;\n line: number;\n };\n };\n dataFlows: CodeFindingDataFlowSuccessResponseData[];\n severityRating: number;\n confidenceRating: number;\n ageRating: number;\n rating: number;\n almIssues: {\n jira: {\n issueId: string;\n project: string;\n };\n azure: {\n workItemId: number;\n project: string;\n };\n jiraPlatform: {\n internalStatus: string;\n issueStatus: string;\n issueKey: string;\n publicLink: string;\n createdTime: string;\n createdBy: string;\n createdByName: string;\n };\n };\n comments: unknown[];\n};\n\nexport type GetCodeFindingSuccessResponseData = {\n response: CodeFindingSuccessResponseData[];\n supportToken: string;\n} & PaginationSuccessResponseData;\n\nexport type GetCodeFindingsRequestData = {\n queryParams?: PaginationQueryParams;\n pathParams: PathParams;\n};\n\n// Dependencies Finding API Data\nexport type DependenciesFindingSuccessResponseData = {\n uuid: string;\n name: string;\n type: string;\n component: {\n uuid: string;\n name: string;\n description: string;\n componentType: string;\n libraryType: string;\n rootLibrary: boolean;\n references: {\n url: string;\n homePage: string;\n genericPackageIndex: string;\n };\n groupId: string;\n artifactId: string;\n version: string;\n path: string;\n };\n findingInfo: {\n status: string;\n comment: unknown;\n detectedAt: string;\n modifiedAt: string;\n };\n project: {\n uuid: string;\n name: string;\n path: string;\n applicationUuid: string;\n };\n application: {\n uuid: string;\n name: string;\n };\n vulnerability: {\n name: string;\n type: string;\n description: string;\n score: number;\n severity: StatisticsName;\n publishDate: string;\n modifiedDate: string;\n vulnerabilityScoring: {\n score: number;\n severity: string;\n type: string;\n }[];\n };\n topFix: {\n id: number;\n vulnerability: string;\n type: string;\n origin: string;\n url: string;\n fixResolution: string;\n date: string;\n message: string;\n };\n effective: string;\n threatAssessment: {\n exploitCodeMaturity: string;\n epssPercentage: number;\n };\n exploitable: boolean;\n scoreMetadataVector: string;\n};\n\nexport type GetDependenciesFindingSuccessResponseData = {\n supportToken: string;\n response: DependenciesFindingSuccessResponseData[];\n} & PaginationSuccessResponseData;\n\nexport type GetDependenciesFindingsRequestData = {\n queryParams?: PaginationQueryParams;\n pathParams: PathParams;\n};\n\n// Containers Finding API Data\nexport type ContainersFindingSuccessResponseData = {\n uuid: string;\n vulnerabilityId: string;\n description: string;\n projectUuid: string;\n imageName: string;\n packageName: string;\n packageVersion: string;\n severity: StatisticsName;\n cvss: number;\n epss: number;\n hasFix: false;\n fixVersion: string;\n publishedDate: string;\n detectionDate: string;\n};\n\nexport type GetContainersFindingSuccessResponseData = {\n supportToken: string;\n response: ContainersFindingSuccessResponseData[];\n} & PaginationSuccessResponseData;\n\nexport type GetContainersFindingsRequestData = {\n queryParams?: PaginationQueryParams;\n pathParams: PathParams;\n};\n\nexport type Finding = {\n kind: StatisticsEngine;\n level: StatisticsName;\n name: string;\n origin: string;\n time: string;\n projectId: string;\n projectName: string;\n issue: {\n issueStatus: string;\n reporter: string;\n creationDate: string;\n ticketName: string;\n link: string;\n status: string;\n };\n};\n"],"names":["StatisticsEngine"],"mappings":";;AAqKY,IAAA,gBAAA,qBAAAA,iBAAL,KAAA;AACL,EAAAA,kBAAA,cAAe,CAAA,GAAA,cAAA;AACf,EAAAA,kBAAA,MAAO,CAAA,GAAA,MAAA;AACP,EAAAA,kBAAA,YAAa,CAAA,GAAA,YAAA;AAHH,EAAAA,OAAAA,iBAAAA;AAAA,CAAA,EAAA,gBAAA,IAAA,EAAA;;;;"}
|
|
1
|
+
{"version":3,"file":"data.service.types.cjs.js","sources":["../../src/service/data.service.types.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport type PaginationQueryParams = {\n cursor?: string;\n limit?: string;\n};\n\ntype PaginationSuccessResponseData = {\n additionalData: {\n totalItems: number;\n paging: {\n next?: string;\n };\n };\n};\n\ntype BodyParams = {\n projectUuids?: string[];\n applicationUuid?: string[];\n};\n\ntype PathParams = {\n uuid: string;\n};\n\nexport type GetOrganizationProjectRequestData = {\n queryParams?: PaginationQueryParams;\n};\n\nexport type OrganizationProjectSuccessResponseData = {\n uuid: string;\n name: string;\n path: string;\n applicationName: string;\n applicationUuid: string;\n};\n\nexport type GetOrganizationProjectSuccessResponseData = {\n supportToken: string;\n response: OrganizationProjectSuccessResponseData[];\n} & PaginationSuccessResponseData;\n\nexport type GetProjectStatisticsRequestData = {\n queryParams?: PaginationQueryParams;\n bodyParams?: BodyParams;\n};\n\nexport type ProjectStatisticsSuccessResponseData = {\n uuid: string;\n name: string;\n path: string;\n applicationUuid: string;\n creationDate: string;\n tags: [];\n labels: [];\n statistics: {\n UNIFIED_VULNERABILITIES: {\n unifiedCriticalVulnerabilities: number;\n unifiedHighVulnerabilities: number;\n unifiedMediumVulnerabilities: number;\n unifiedLowVulnerabilities: number;\n unifiedVulnerabilities: number;\n };\n VULNERABILITY_EFFECTIVENESS: {};\n LIBRARY_TYPE_HISTOGRAM: Record<string, number>;\n IMG_USAGE: {};\n POLICY_VIOLATION_LIBRARIES: {\n policyViolatingLibraries: number;\n };\n SAST_VULNERABILITIES_BY_TYPE: Record<string, number>;\n GENERAL: {\n totalLibraries: number;\n };\n LLM_SECURITY: {\n llmTotalLines: number;\n };\n IMG_SECURITY: {\n imgCriticalVulnerabilities: number;\n imgMaxRiskScore: number;\n imgMediumVulnerabilities: number;\n imgLowVulnerabilities: number;\n imgSecretMediumVulnerabilities: number;\n imgUnknownVulnerabilities: number;\n imgSecretHighVulnerabilities: number;\n imgTotalVulnerabilities: number;\n imgHighVulnerabilities: number;\n imgSecretCriticalVulnerabilities: number;\n imgSecretLowVulnerabilities: number;\n };\n ALERTS: {\n criticalSeverityVulnerabilities: number;\n highSeverityVulnerabilities: number;\n vulnerableLibraries: number;\n mediumSeverityVulnerabilities: number;\n lowSeverityVulnerabilities: number;\n };\n OUTDATED_LIBRARIES: {\n outdatedLibraries: number;\n };\n POLICY_VIOLATIONS: {};\n SAST_SCAN: {\n sastTotalLines: number;\n sastTestedFiles: number;\n sastTotalFiles: number;\n sastTestedLines: number;\n sastTotalMended: number;\n sastTotalRemediations: number;\n };\n VULNERABILITY_SEVERITY_LIBRARIES: {\n lowSeverityLibraries: number;\n highSeverityLibraries: number;\n mediumSeverityLibraries: number;\n criticalSeverityLibraries: number;\n };\n LICENSE_RISK: {\n highRiskLicenses: number;\n mediumRiskLicenses: number;\n lowRiskLicenses: number;\n };\n IAC_SECURITY: {\n iacCriticalMisconfigurations: number;\n iacHighMisconfigurations: number;\n iacTotalMisconfigurations: number;\n iacLowMisconfigurations: number;\n iacMediumMisconfigurations: number;\n };\n SCA_SECURITY: {};\n LICENSE_HISTOGRAM: Record<string, number>;\n SAST_VULNERABILITIES_BY_SEVERITY: {\n sastVulnerabilities: number;\n sastHighVulnerabilities: number;\n sastMediumVulnerabilities: number;\n sastLowVulnerabilities: number;\n };\n LAST_SCAN: {\n lastScanTime: number;\n lastScaScanTime: number;\n lastImgScanTime: number;\n lastSastScanTime: number;\n };\n };\n};\n\nexport type GetProjectStatisticsSuccessResponseData = {\n supportToken: string;\n response: ProjectStatisticsSuccessResponseData[];\n} & PaginationSuccessResponseData;\n\nexport type EntityURL = {\n path: string;\n params: {\n org?: string;\n repo?: string;\n };\n namespace?: string;\n kind: string;\n source: string;\n};\n\nexport enum StatisticsName {\n CRITICAL = 'critical',\n HIGH = 'high',\n MEDIUM = 'medium',\n LOW = 'low',\n TOTAL = 'total',\n}\n\nexport enum StatisticsEngine {\n DEPENDENCIES = 'dependencies',\n CODE = 'code',\n CONTAINERS = 'containers',\n}\n\ntype StatisticsBase = {\n [StatisticsName.CRITICAL]: number;\n [StatisticsName.HIGH]: number;\n [StatisticsName.MEDIUM]: number;\n [StatisticsName.LOW]: number;\n [StatisticsName.TOTAL]: number;\n};\n\nexport type Statistics = {\n [StatisticsEngine.DEPENDENCIES]: StatisticsBase;\n [StatisticsEngine.CODE]: Omit<StatisticsBase, StatisticsName.CRITICAL> & {\n [StatisticsName.CRITICAL]: null;\n };\n [StatisticsEngine.CONTAINERS]: StatisticsBase;\n} & StatisticsBase;\n\nexport type Project = {\n statistics: Statistics;\n uuid: string;\n name: string;\n path: string;\n applicationName: string;\n applicationUuid: string;\n lastScan: number;\n languages: Array<[string, number]>;\n entity: EntityURL;\n};\n\n// Code Finding API Data\ntype CodeFindingDataFlowSuccessResponseData = {\n id: string;\n sink: string;\n sinkKind: string;\n sinkFile: string;\n sinkSnippet: string;\n sinkLine: number;\n inputSource: string;\n inputKind: string;\n inputFlow: [\n {\n name: string;\n kind: string;\n file: string;\n snippet: string;\n line: number;\n startLine: number;\n endLine: number;\n },\n ];\n functionCalls: [\n {\n name: string;\n kind: string;\n file: string;\n snippet: string;\n line: number;\n startLine: number;\n endLine: number;\n },\n ];\n filter: {\n isFiltered: boolean;\n filterTypes: unknown[];\n };\n isNew: boolean;\n rating: number;\n confidenceRating: number;\n ageRating: number;\n};\n\nexport type CodeFindingSuccessResponseData = {\n id: string;\n scanId: string;\n snapshotId: string;\n projectId: string;\n appId: string;\n type: {\n id: number;\n name: string;\n engineId: number;\n language: string;\n sarif: string;\n sarifLevel: string;\n order: number;\n severity: StatisticsName;\n severityRating: number;\n description: string;\n recommendations: [string];\n references: [string];\n cwe: {\n id: string;\n title: string;\n url: string;\n };\n pcidss: {\n section: string;\n title: string;\n };\n nist: {\n control: string;\n priority: string;\n title: string;\n url: string;\n };\n hipaa: {\n control: string;\n title: string;\n };\n hitrust: {\n control: string;\n title: string;\n };\n owasp: {\n index: string;\n title: string;\n url: string;\n };\n owasp2021: {\n index: string;\n title: string;\n url: string;\n };\n capec: {\n id: string;\n title: string;\n url: string;\n };\n sansTop25: {\n rank: number;\n title: string;\n };\n };\n description: string;\n createdTime: string;\n isNew: boolean;\n severity: StatisticsName;\n baseline: boolean;\n hasRemediation: boolean;\n suppressed: boolean;\n suppressedBy: string;\n suppressionTime: string;\n suppressionMessage: string;\n reviewed: boolean;\n IssueStatus: number;\n sharedStep: {\n name: string;\n kind: string;\n file: string;\n snippet: string;\n line: number;\n startLine: number;\n endLine: number;\n lineBlame: {\n commitId: string;\n file: string;\n line: number;\n };\n };\n dataFlows: CodeFindingDataFlowSuccessResponseData[];\n severityRating: number;\n confidenceRating: number;\n ageRating: number;\n rating: number;\n almIssues: {\n jira: {\n issueId: string;\n project: string;\n };\n azure: {\n workItemId: number;\n project: string;\n };\n jiraPlatform: {\n internalStatus: string;\n issueStatus: string;\n issueKey: string;\n publicLink: string;\n createdTime: string;\n createdBy: string;\n createdByName: string;\n };\n };\n comments: unknown[];\n};\n\nexport type GetCodeFindingSuccessResponseData = {\n response: CodeFindingSuccessResponseData[];\n supportToken: string;\n} & PaginationSuccessResponseData;\n\nexport type GetCodeFindingsRequestData = {\n queryParams?: PaginationQueryParams;\n pathParams: PathParams;\n};\n\n// Dependencies Finding API Data\nexport type DependenciesFindingSuccessResponseData = {\n uuid: string;\n name: string;\n type: string;\n component: {\n uuid: string;\n name: string;\n description: string;\n componentType: string;\n libraryType: string;\n rootLibrary: boolean;\n references: {\n url: string;\n homePage: string;\n genericPackageIndex: string;\n };\n groupId: string;\n artifactId: string;\n version: string;\n path: string;\n };\n findingInfo: {\n status: string;\n comment: unknown;\n detectedAt: string;\n modifiedAt: string;\n };\n project: {\n uuid: string;\n name: string;\n path: string;\n applicationUuid: string;\n };\n application: {\n uuid: string;\n name: string;\n };\n vulnerability: {\n name: string;\n type: string;\n description: string;\n score: number;\n severity: StatisticsName;\n publishDate: string;\n modifiedDate: string;\n vulnerabilityScoring: {\n score: number;\n severity: string;\n type: string;\n }[];\n };\n topFix: {\n id: number;\n vulnerability: string;\n type: string;\n origin: string;\n url: string;\n fixResolution: string;\n date: string;\n message: string;\n };\n effective: string;\n threatAssessment: {\n exploitCodeMaturity: string;\n epssPercentage: number;\n };\n exploitable: boolean;\n scoreMetadataVector: string;\n};\n\nexport type GetDependenciesFindingSuccessResponseData = {\n supportToken: string;\n response: DependenciesFindingSuccessResponseData[];\n} & PaginationSuccessResponseData;\n\nexport type GetDependenciesFindingsRequestData = {\n queryParams?: PaginationQueryParams;\n pathParams: PathParams;\n};\n\n// Containers Finding API Data\nexport type ContainersFindingSuccessResponseData = {\n uuid: string;\n vulnerabilityId: string;\n description: string;\n projectUuid: string;\n imageName: string;\n packageName: string;\n packageVersion: string;\n severity: StatisticsName;\n cvss: number;\n epss: number;\n hasFix: false;\n fixVersion: string;\n publishedDate: string;\n detectionDate: string;\n};\n\nexport type GetContainersFindingSuccessResponseData = {\n supportToken: string;\n response: ContainersFindingSuccessResponseData[];\n} & PaginationSuccessResponseData;\n\nexport type GetContainersFindingsRequestData = {\n queryParams?: PaginationQueryParams;\n pathParams: PathParams;\n};\n\nexport type Finding = {\n kind: StatisticsEngine;\n level: StatisticsName;\n name: string;\n origin: string;\n time: string;\n projectId: string;\n projectName: string;\n issue: {\n issueStatus: string;\n reporter: string;\n creationDate: string;\n ticketName: string;\n link: string;\n status: string;\n };\n};\n"],"names":["StatisticsEngine"],"mappings":";;AAoLO,IAAK,gBAAA,qBAAAA,iBAAAA,KAAL;AACL,EAAAA,kBAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,kBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,kBAAA,YAAA,CAAA,GAAa,YAAA;AAHH,EAAA,OAAAA,iBAAAA;AAAA,CAAA,EAAA,gBAAA,IAAA,EAAA;;;;"}
|
|
@@ -4,13 +4,9 @@ var express = require('express');
|
|
|
4
4
|
var Router = require('express-promise-router');
|
|
5
5
|
var rootHttpRouter = require('@backstage/backend-defaults/rootHttpRouter');
|
|
6
6
|
var catalogClient = require('@backstage/catalog-client');
|
|
7
|
-
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
8
7
|
var data_service_helpers = require('./data.service.helpers.cjs.js');
|
|
9
8
|
var data_service = require('./data.service.cjs.js');
|
|
10
9
|
var auth_service = require('./auth.service.cjs.js');
|
|
11
|
-
var permissions = require('../permission/permissions.cjs.js');
|
|
12
|
-
var conditions = require('../permission/conditions.cjs.js');
|
|
13
|
-
require('../permission/rules.cjs.js');
|
|
14
10
|
var constants = require('../constants.cjs.js');
|
|
15
11
|
|
|
16
12
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
@@ -19,11 +15,17 @@ var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
|
19
15
|
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
20
16
|
|
|
21
17
|
async function createRouter(options) {
|
|
22
|
-
const { logger, config, discovery, auth, httpAuth
|
|
18
|
+
const { logger, config, discovery, auth, httpAuth } = options;
|
|
23
19
|
const router = Router__default.default();
|
|
24
20
|
router.use(express__default.default.json());
|
|
25
|
-
router.use(conditions.permissionIntegrationRouter);
|
|
26
21
|
const checkForAuth = (_request, response, next) => {
|
|
22
|
+
if (!auth_service.MendAuthSevice.isConfigured()) {
|
|
23
|
+
response.status(401).json({
|
|
24
|
+
name: "ConfigurationError",
|
|
25
|
+
message: auth_service.MendAuthSevice.getConfigurationError()
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
27
29
|
if (auth_service.MendAuthSevice.getAuthToken()) {
|
|
28
30
|
next();
|
|
29
31
|
return;
|
|
@@ -34,12 +36,35 @@ async function createRouter(options) {
|
|
|
34
36
|
response.status(err?.status || 401).json({ error: err?.statusText || "Oops! Unauthorized" });
|
|
35
37
|
});
|
|
36
38
|
};
|
|
37
|
-
const activationKey = config.
|
|
39
|
+
const activationKey = config.getOptionalString("mend.activationKey") ?? "";
|
|
40
|
+
auth_service.MendAuthSevice.init({
|
|
41
|
+
apiVersion: constants.MEND_API_VERSION,
|
|
42
|
+
activationKey,
|
|
43
|
+
logger
|
|
44
|
+
});
|
|
38
45
|
const mendDataService = new data_service.MendDataService({
|
|
39
46
|
apiVersion: constants.MEND_API_VERSION,
|
|
40
|
-
activationKey
|
|
47
|
+
activationKey,
|
|
48
|
+
logger
|
|
41
49
|
});
|
|
42
50
|
const catalogClient$1 = new catalogClient.CatalogClient({ discoveryApi: discovery });
|
|
51
|
+
const filterProjectsByPermissionControl = (projectItems) => {
|
|
52
|
+
const permissionControl = config.getOptionalConfig(
|
|
53
|
+
"mend.permissionControl"
|
|
54
|
+
);
|
|
55
|
+
if (!permissionControl) {
|
|
56
|
+
return projectItems;
|
|
57
|
+
}
|
|
58
|
+
const ids = permissionControl.getOptionalStringArray("ids") || [];
|
|
59
|
+
const exclude = permissionControl.getOptionalBoolean("exclude") ?? true;
|
|
60
|
+
if (ids.length === 0) {
|
|
61
|
+
return projectItems;
|
|
62
|
+
}
|
|
63
|
+
return projectItems.filter((item) => {
|
|
64
|
+
const isInList = ids.includes(item.uuid);
|
|
65
|
+
return exclude ? !isInList : isInList;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
43
68
|
router.get("/project" /* PROJECT */, checkForAuth, async (request, response) => {
|
|
44
69
|
try {
|
|
45
70
|
const credentials = await httpAuth.credentials(request);
|
|
@@ -59,20 +84,8 @@ async function createRouter(options) {
|
|
|
59
84
|
mendDataService.getOrganizationProject
|
|
60
85
|
)
|
|
61
86
|
]);
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
credentials
|
|
66
|
-
}
|
|
67
|
-
))[0];
|
|
68
|
-
let items;
|
|
69
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
70
|
-
const filter = conditions.transformConditions(decision.conditions);
|
|
71
|
-
items = results[1].filter(
|
|
72
|
-
(item) => filter?.exclude ? !filter.ids.includes(item.uuid) : filter.ids.includes(item.uuid)
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
const data = data_service_helpers.dataMatcher(results[0].items, items || results[1]);
|
|
87
|
+
const filteredItems = filterProjectsByPermissionControl(results[1]);
|
|
88
|
+
const data = data_service_helpers.dataMatcher(results[0].items, filteredItems);
|
|
76
89
|
const projects = data_service_helpers.dataProjectParser(data, results[2]);
|
|
77
90
|
response.json({
|
|
78
91
|
...projects,
|
|
@@ -93,7 +106,11 @@ async function createRouter(options) {
|
|
|
93
106
|
});
|
|
94
107
|
const uid = request.body.uid;
|
|
95
108
|
if (!uid) {
|
|
96
|
-
response.status(
|
|
109
|
+
response.status(400).json({
|
|
110
|
+
message: "Oops! No UUID provided",
|
|
111
|
+
clientUrl: auth_service.MendAuthSevice.getClientUrl(),
|
|
112
|
+
clientName: auth_service.MendAuthSevice.getClientName()
|
|
113
|
+
});
|
|
97
114
|
return;
|
|
98
115
|
}
|
|
99
116
|
const projectResult = await Promise.all([
|
|
@@ -108,32 +125,15 @@ async function createRouter(options) {
|
|
|
108
125
|
mendDataService.getOrganizationProject
|
|
109
126
|
)
|
|
110
127
|
]);
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
{
|
|
114
|
-
credentials
|
|
115
|
-
}
|
|
116
|
-
))[0];
|
|
117
|
-
let items;
|
|
118
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
119
|
-
const filter = conditions.transformConditions(decision.conditions);
|
|
120
|
-
items = projectResult[1].filter(
|
|
121
|
-
(item) => filter?.exclude ? !filter.ids.includes(item.uuid) : filter.ids.includes(item.uuid)
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
const data = data_service_helpers.dataMatcher(
|
|
125
|
-
projectResult[0].items,
|
|
126
|
-
items || projectResult[1]
|
|
127
|
-
);
|
|
128
|
+
const filteredItems = filterProjectsByPermissionControl(projectResult[1]);
|
|
129
|
+
const data = data_service_helpers.dataMatcher(projectResult[0].items, filteredItems);
|
|
128
130
|
const entityURL = data_service_helpers.parseEntityURL(
|
|
129
131
|
projectResult[0].items[0]?.metadata?.annotations?.["backstage.io/source-location"]
|
|
130
132
|
);
|
|
131
133
|
const projectSourceUrl = entityURL?.host ? `${entityURL.host}${entityURL.path}` : "";
|
|
132
134
|
if (!data.length) {
|
|
133
|
-
response.json({
|
|
134
|
-
|
|
135
|
-
projectList: [],
|
|
136
|
-
projectSourceUrl,
|
|
135
|
+
response.status(404).json({
|
|
136
|
+
message: "Results for this repository are either unavailable on Mend or can not be accessed.",
|
|
137
137
|
clientUrl: auth_service.MendAuthSevice.getClientUrl(),
|
|
138
138
|
clientName: auth_service.MendAuthSevice.getClientName()
|
|
139
139
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["import express from 'express';\nimport Router from 'express-promise-router';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport {\n LoggerService,\n DiscoveryService,\n AuthService,\n HttpAuthService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { Config } from '@backstage/config';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\nimport {\n dataFindingParser,\n dataMatcher,\n dataProjectParser,\n fetchQueryPagination,\n parseEntityURL,\n} from './data.service.helpers';\nimport { MendDataService } from './data.service';\nimport { MendAuthSevice } from './auth.service';\nimport {\n PaginationQueryParams,\n ProjectStatisticsSuccessResponseData,\n OrganizationProjectSuccessResponseData,\n CodeFindingSuccessResponseData,\n DependenciesFindingSuccessResponseData,\n ContainersFindingSuccessResponseData,\n Finding,\n} from './data.service.types';\nimport {\n mendReadPermission,\n transformConditions,\n permissionIntegrationRouter,\n type FilterProps,\n} from '../permission';\nimport { MEND_API_VERSION } from '../constants';\n\n/** @internal */\nexport type RouterOptions = {\n logger: LoggerService;\n config: Config;\n discovery: DiscoveryService;\n auth: AuthService;\n httpAuth: HttpAuthService;\n permissions: PermissionsService;\n};\n\nenum ROUTE {\n PROJECT = '/project',\n FINDING = '/finding',\n}\n\n/** @internal */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, config, discovery, auth, httpAuth, permissions } = options;\n\n const router = Router();\n router.use(express.json());\n\n router.use(permissionIntegrationRouter);\n\n const checkForAuth = (\n _request: express.Request,\n response: express.Response,\n next: express.NextFunction,\n ) => {\n if (MendAuthSevice.getAuthToken()) {\n next();\n return;\n }\n\n MendAuthSevice.connect()\n .then(next)\n .catch(err => {\n const errorMessage =\n err instanceof Error ? err?.message : err?.statusText;\n logger.error(errorMessage || 'Oops! Unauthorized');\n response\n .status(err?.status || 401)\n .json({ error: err?.statusText || 'Oops! Unauthorized' });\n });\n };\n\n const activationKey = config.getString('mend.activationKey');\n\n // Init api service\n const mendDataService = new MendDataService({\n apiVersion: MEND_API_VERSION,\n activationKey,\n });\n\n // Init catalog client\n const catalogClient = new CatalogClient({ discoveryApi: discovery });\n\n // Routes\n router.get(ROUTE.PROJECT, checkForAuth, async (request, response) => {\n try {\n // service to service auth\n const credentials = await httpAuth.credentials(request);\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog',\n });\n\n // entity to project match\n const results = await Promise.all([\n catalogClient.getEntities(\n { filter: [{ kind: ['Component'] }] },\n { token },\n ),\n fetchQueryPagination<ProjectStatisticsSuccessResponseData>(\n mendDataService.getProjectStatistics,\n ),\n fetchQueryPagination<OrganizationProjectSuccessResponseData>(\n mendDataService.getOrganizationProject,\n ),\n ]);\n\n // permission - filter to exclude or include project\n const decision = (\n await permissions.authorizeConditional(\n [{ permission: mendReadPermission }],\n {\n credentials,\n },\n )\n )[0];\n\n let items;\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n const filter = transformConditions(decision.conditions) as FilterProps;\n items = results[1].filter(item =>\n filter?.exclude\n ? !filter.ids.includes(item.uuid)\n : filter.ids.includes(item.uuid),\n );\n }\n\n const data = dataMatcher(results[0].items, items || results[1]);\n\n // parse data\n const projects = dataProjectParser(data, results[2]);\n\n response.json({\n ...projects,\n clientUrl: MendAuthSevice.getClientUrl(),\n clientName: MendAuthSevice.getClientName(),\n });\n // Allow any object structure here\n } catch (error: any) {\n logger.error('/project', error);\n response.status(500).json({ error: 'Oops! Please try again later.' });\n }\n });\n\n router.post(ROUTE.FINDING, checkForAuth, async (request, response) => {\n try {\n // service to service auth\n const credentials = await httpAuth.credentials(request);\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog',\n });\n\n // entity to project match\n const uid = request.body.uid;\n\n if (!uid) {\n response.status(401).json({ error: 'Oops! No UUID provided' });\n return;\n }\n\n const projectResult = await Promise.all([\n catalogClient.getEntities(\n { filter: [{ 'metadata.uid': uid }] },\n { token },\n ),\n fetchQueryPagination<ProjectStatisticsSuccessResponseData>(\n mendDataService.getProjectStatistics,\n ),\n fetchQueryPagination<OrganizationProjectSuccessResponseData>(\n mendDataService.getOrganizationProject,\n ),\n ]);\n\n // permission - filter to exclude or include project\n const decision = (\n await permissions.authorizeConditional(\n [{ permission: mendReadPermission }],\n {\n credentials,\n },\n )\n )[0];\n\n let items;\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n const filter = transformConditions(decision.conditions) as FilterProps;\n items = projectResult[1].filter(item =>\n filter?.exclude\n ? !filter.ids.includes(item.uuid)\n : filter.ids.includes(item.uuid),\n );\n }\n\n const data = dataMatcher(\n projectResult[0].items,\n items || projectResult[1],\n );\n\n const entityURL = parseEntityURL(\n projectResult[0].items[0]?.metadata?.annotations?.[\n 'backstage.io/source-location'\n ],\n );\n\n const projectSourceUrl = entityURL?.host\n ? `${entityURL.host}${entityURL.path}`\n : '';\n\n if (!data.length) {\n response.json({\n findingList: [],\n projectList: [],\n projectSourceUrl,\n clientUrl: MendAuthSevice.getClientUrl(),\n clientName: MendAuthSevice.getClientName(),\n });\n return;\n }\n\n const findingList: Finding[] = [];\n\n for (const projectItem of data) {\n const params = {\n pathParams: {\n uuid: projectItem.uuid,\n },\n };\n\n // get project findings\n const findingResult = await Promise.all([\n fetchQueryPagination<CodeFindingSuccessResponseData>(\n (queryParam: PaginationQueryParams) =>\n mendDataService.getCodeFinding({\n ...params,\n ...queryParam,\n }),\n ),\n fetchQueryPagination<DependenciesFindingSuccessResponseData>(\n (queryParam: PaginationQueryParams) =>\n mendDataService.getDependenciesFinding({\n ...params,\n ...queryParam,\n }),\n ),\n fetchQueryPagination<ContainersFindingSuccessResponseData>(\n (queryParam: PaginationQueryParams) =>\n mendDataService.getContainersFinding({\n ...params,\n ...queryParam,\n }),\n ),\n ]);\n\n const tempFindingList: Finding[] = dataFindingParser(\n findingResult[0].filter(item => !item.suppressed), // NOTE: Do not show suppressed item\n findingResult[1].filter(\n item => !(item.findingInfo.status === 'IGNORED'),\n projectItem,\n ), // NOTE: Do not show ignored item\n findingResult[2], // ESC-51: Follow Jira activity\n projectItem.name,\n );\n findingList.push(...tempFindingList);\n }\n\n const projects = dataProjectParser(data, projectResult[2]);\n\n response.json({\n findingList,\n ...projects,\n projectSourceUrl,\n clientUrl: MendAuthSevice.getClientUrl(),\n clientName: MendAuthSevice.getClientName(),\n });\n // Allow any object structure here\n } catch (error: any) {\n logger.error('/finding', error);\n response.status(500).json({ error: 'Oops! Please try again later.' });\n }\n });\n\n router.get('/health', (_, response) => {\n logger.info('PONG!');\n response.json({ status: 'ok' });\n });\n\n const middleware = MiddlewareFactory.create({ logger, config });\n\n router.use(middleware.error());\n return router;\n}\n"],"names":["permissions","Router","express","permissionIntegrationRouter","MendAuthSevice","MendDataService","MEND_API_VERSION","catalogClient","CatalogClient","fetchQueryPagination","mendReadPermission","AuthorizeResult","transformConditions","dataMatcher","dataProjectParser","parseEntityURL","dataFindingParser","MiddlewareFactory"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport {\n LoggerService,\n DiscoveryService,\n AuthService,\n HttpAuthService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport type { Config } from '@backstage/config';\nimport {\n dataFindingParser,\n dataMatcher,\n dataProjectParser,\n fetchQueryPagination,\n parseEntityURL,\n} from './data.service.helpers';\nimport { MendDataService } from './data.service';\nimport { MendAuthSevice } from './auth.service';\nimport {\n PaginationQueryParams,\n ProjectStatisticsSuccessResponseData,\n OrganizationProjectSuccessResponseData,\n CodeFindingSuccessResponseData,\n DependenciesFindingSuccessResponseData,\n ContainersFindingSuccessResponseData,\n Finding,\n} from './data.service.types';\nimport { MEND_API_VERSION } from '../constants';\n\n/** @internal */\nexport type RouterOptions = {\n logger: LoggerService;\n config: Config;\n discovery: DiscoveryService;\n auth: AuthService;\n httpAuth: HttpAuthService;\n};\n\nenum ROUTE {\n PROJECT = '/project',\n FINDING = '/finding',\n}\n\n/** @internal */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const { logger, config, discovery, auth, httpAuth } = options;\n\n const router = Router();\n router.use(express.json());\n\n const checkForAuth = (\n _request: express.Request,\n response: express.Response,\n next: express.NextFunction,\n ) => {\n // Check if activation key is configured\n if (!MendAuthSevice.isConfigured()) {\n response.status(401).json({\n name: 'ConfigurationError',\n message: MendAuthSevice.getConfigurationError(),\n });\n return;\n }\n\n if (MendAuthSevice.getAuthToken()) {\n next();\n return;\n }\n\n MendAuthSevice.connect()\n .then(next)\n .catch(err => {\n const errorMessage =\n err instanceof Error ? err?.message : err?.statusText;\n logger.error(errorMessage || 'Oops! Unauthorized');\n response\n .status(err?.status || 401)\n .json({ error: err?.statusText || 'Oops! Unauthorized' });\n });\n };\n\n const activationKey = config.getOptionalString('mend.activationKey') ?? '';\n\n // Init auth service with logger (logs warning if activation key is missing/invalid)\n MendAuthSevice.init({\n apiVersion: MEND_API_VERSION,\n activationKey,\n logger,\n });\n\n // Init api service\n const mendDataService = new MendDataService({\n apiVersion: MEND_API_VERSION,\n activationKey,\n logger,\n });\n\n // Init catalog client\n const catalogClient = new CatalogClient({ discoveryApi: discovery });\n\n /**\n * Filters project IDs based on mend.permissionControl configuration\n * @param projectItems - Array of project items to filter\n * @returns Filtered array of project items\n */\n const filterProjectsByPermissionControl = <T extends { uuid: string }>(\n projectItems: T[],\n ): T[] => {\n const permissionControl = config.getOptionalConfig(\n 'mend.permissionControl',\n );\n\n if (!permissionControl) {\n // No permission control configured, return all items\n return projectItems;\n }\n\n const ids = permissionControl.getOptionalStringArray('ids') || [];\n const exclude = permissionControl.getOptionalBoolean('exclude') ?? true;\n\n if (ids.length === 0) {\n // No IDs configured, return all items\n return projectItems;\n }\n\n return projectItems.filter(item => {\n const isInList = ids.includes(item.uuid);\n // If exclude is true (blocklist mode): filter out items in the list\n // If exclude is false (allowlist mode): only include items in the list\n return exclude ? !isInList : isInList;\n });\n };\n\n // Routes\n router.get(ROUTE.PROJECT, checkForAuth, async (request, response) => {\n try {\n // service to service auth\n const credentials = await httpAuth.credentials(request);\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog',\n });\n\n // entity to project match\n const results = await Promise.all([\n catalogClient.getEntities(\n { filter: [{ kind: ['Component'] }] },\n { token },\n ),\n fetchQueryPagination<ProjectStatisticsSuccessResponseData>(\n mendDataService.getProjectStatistics,\n ),\n fetchQueryPagination<OrganizationProjectSuccessResponseData>(\n mendDataService.getOrganizationProject,\n ),\n ]);\n\n // Apply permission control from config\n const filteredItems = filterProjectsByPermissionControl(results[1]);\n\n const data = dataMatcher(results[0].items, filteredItems);\n\n // parse data\n const projects = dataProjectParser(data, results[2]);\n\n response.json({\n ...projects,\n clientUrl: MendAuthSevice.getClientUrl(),\n clientName: MendAuthSevice.getClientName(),\n });\n // Allow any object structure here\n } catch (error: any) {\n logger.error('/project', error);\n response.status(500).json({ error: 'Oops! Please try again later.' });\n }\n });\n\n router.post(ROUTE.FINDING, checkForAuth, async (request, response) => {\n try {\n // service to service auth\n const credentials = await httpAuth.credentials(request);\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: 'catalog',\n });\n\n // entity to project match\n const uid = request.body.uid;\n\n if (!uid) {\n response.status(400).json({\n message: 'Oops! No UUID provided',\n clientUrl: MendAuthSevice.getClientUrl(),\n clientName: MendAuthSevice.getClientName(),\n });\n return;\n }\n\n const projectResult = await Promise.all([\n catalogClient.getEntities(\n { filter: [{ 'metadata.uid': uid }] },\n { token },\n ),\n fetchQueryPagination<ProjectStatisticsSuccessResponseData>(\n mendDataService.getProjectStatistics,\n ),\n fetchQueryPagination<OrganizationProjectSuccessResponseData>(\n mendDataService.getOrganizationProject,\n ),\n ]);\n\n // Apply permission control from config\n const filteredItems = filterProjectsByPermissionControl(projectResult[1]);\n\n const data = dataMatcher(projectResult[0].items, filteredItems);\n\n const entityURL = parseEntityURL(\n projectResult[0].items[0]?.metadata?.annotations?.[\n 'backstage.io/source-location'\n ],\n );\n\n const projectSourceUrl = entityURL?.host\n ? `${entityURL.host}${entityURL.path}`\n : '';\n\n if (!data.length) {\n response.status(404).json({\n message:\n 'Results for this repository are either unavailable on Mend or can not be accessed.',\n clientUrl: MendAuthSevice.getClientUrl(),\n clientName: MendAuthSevice.getClientName(),\n });\n return;\n }\n\n const findingList: Finding[] = [];\n\n for (const projectItem of data) {\n const params = {\n pathParams: {\n uuid: projectItem.uuid,\n },\n };\n\n // get project findings\n const findingResult = await Promise.all([\n fetchQueryPagination<CodeFindingSuccessResponseData>(\n (queryParam: PaginationQueryParams) =>\n mendDataService.getCodeFinding({\n ...params,\n ...queryParam,\n }),\n ),\n fetchQueryPagination<DependenciesFindingSuccessResponseData>(\n (queryParam: PaginationQueryParams) =>\n mendDataService.getDependenciesFinding({\n ...params,\n ...queryParam,\n }),\n ),\n fetchQueryPagination<ContainersFindingSuccessResponseData>(\n (queryParam: PaginationQueryParams) =>\n mendDataService.getContainersFinding({\n ...params,\n ...queryParam,\n }),\n ),\n ]);\n\n const tempFindingList: Finding[] = dataFindingParser(\n findingResult[0].filter(item => !item.suppressed), // NOTE: Do not show suppressed item\n findingResult[1].filter(\n item => !(item.findingInfo.status === 'IGNORED'),\n projectItem,\n ), // NOTE: Do not show ignored item\n findingResult[2], // ESC-51: Follow Jira activity\n projectItem.name,\n );\n findingList.push(...tempFindingList);\n }\n\n const projects = dataProjectParser(data, projectResult[2]);\n\n response.json({\n findingList,\n ...projects,\n projectSourceUrl,\n clientUrl: MendAuthSevice.getClientUrl(),\n clientName: MendAuthSevice.getClientName(),\n });\n // Allow any object structure here\n } catch (error: any) {\n logger.error('/finding', error);\n response.status(500).json({ error: 'Oops! Please try again later.' });\n }\n });\n\n router.get('/health', (_, response) => {\n logger.info('PONG!');\n response.json({ status: 'ok' });\n });\n\n const middleware = MiddlewareFactory.create({ logger, config });\n\n router.use(middleware.error());\n return router;\n}\n"],"names":["Router","express","MendAuthSevice","MEND_API_VERSION","MendDataService","catalogClient","CatalogClient","fetchQueryPagination","dataMatcher","dataProjectParser","parseEntityURL","dataFindingParser","MiddlewareFactory"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage-community/plugin-mend-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"main": "dist/index.cjs.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@backstage/config": "^1.3.6",
|
|
45
45
|
"@backstage/plugin-permission-common": "^0.9.3",
|
|
46
46
|
"@backstage/plugin-permission-node": "^0.10.7",
|
|
47
|
-
"@types/express": "
|
|
47
|
+
"@types/express": "^4.17.6",
|
|
48
48
|
"express": "^4.17.1",
|
|
49
49
|
"express-promise-router": "^4.1.0",
|
|
50
50
|
"jsonwebtoken": "^9.0.2",
|
|
@@ -59,7 +59,8 @@
|
|
|
59
59
|
"@backstage/cli": "^0.35.1",
|
|
60
60
|
"@backstage/plugin-auth-backend": "^0.25.7",
|
|
61
61
|
"@backstage/plugin-auth-backend-module-guest-provider": "^0.2.15",
|
|
62
|
-
"@
|
|
62
|
+
"@backstage/plugin-catalog-backend": "^3.3.0",
|
|
63
|
+
"@types/supertest": "^6.0.0",
|
|
63
64
|
"msw": "^1.0.0",
|
|
64
65
|
"supertest": "^7.0.0"
|
|
65
66
|
},
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
4
|
-
var rules = require('./rules.cjs.js');
|
|
5
|
-
var permissions = require('./permissions.cjs.js');
|
|
6
|
-
|
|
7
|
-
const { conditions, createConditionalDecision } = pluginPermissionNode.createConditionExports({
|
|
8
|
-
pluginId: "mend",
|
|
9
|
-
resourceType: permissions.RESOURCE_TYPE.PROJECT,
|
|
10
|
-
rules: rules.rules
|
|
11
|
-
});
|
|
12
|
-
const mendConditions = conditions;
|
|
13
|
-
const createMendProjectConditionalDecision = createConditionalDecision;
|
|
14
|
-
const permissionIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
|
|
15
|
-
permissions: [permissions.mendReadPermission],
|
|
16
|
-
getResources: async (resourceRefs) => {
|
|
17
|
-
return resourceRefs.map((resourceRef) => {
|
|
18
|
-
return {
|
|
19
|
-
permission: permissions.mendReadPermission,
|
|
20
|
-
resourceRef
|
|
21
|
-
};
|
|
22
|
-
});
|
|
23
|
-
},
|
|
24
|
-
resourceType: permissions.RESOURCE_TYPE.PROJECT,
|
|
25
|
-
rules: Object.values(rules.rules)
|
|
26
|
-
});
|
|
27
|
-
const transformConditions = pluginPermissionNode.createConditionTransformer(Object.values(rules.rules));
|
|
28
|
-
|
|
29
|
-
exports.createMendProjectConditionalDecision = createMendProjectConditionalDecision;
|
|
30
|
-
exports.mendConditions = mendConditions;
|
|
31
|
-
exports.permissionIntegrationRouter = permissionIntegrationRouter;
|
|
32
|
-
exports.transformConditions = transformConditions;
|
|
33
|
-
//# sourceMappingURL=conditions.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"conditions.cjs.js","sources":["../../src/permission/conditions.ts"],"sourcesContent":["import express from 'express';\nimport { createConditionExports } from '@backstage/plugin-permission-node';\nimport {\n createPermissionIntegrationRouter,\n createConditionTransformer,\n ConditionTransformer,\n} from '@backstage/plugin-permission-node';\nimport { rules, type FilterProps } from './rules';\nimport { RESOURCE_TYPE, mendReadPermission } from './permissions';\n\nconst { conditions, createConditionalDecision } = createConditionExports({\n pluginId: 'mend',\n resourceType: RESOURCE_TYPE.PROJECT,\n rules,\n});\n\n/** @public */\nexport const mendConditions = conditions;\n\n/** @public */\nexport const createMendProjectConditionalDecision = createConditionalDecision;\n\nexport const permissionIntegrationRouter: express.Router =\n createPermissionIntegrationRouter({\n permissions: [mendReadPermission],\n getResources: async resourceRefs => {\n return resourceRefs.map(resourceRef => {\n return {\n permission: mendReadPermission,\n resourceRef,\n };\n });\n },\n resourceType: RESOURCE_TYPE.PROJECT,\n rules: Object.values(rules),\n });\n\nexport const transformConditions: ConditionTransformer<FilterProps> =\n createConditionTransformer(Object.values(rules));\n"],"names":["createConditionExports","RESOURCE_TYPE","rules","createPermissionIntegrationRouter","mendReadPermission","createConditionTransformer"],"mappings":";;;;;;AAUA,MAAM,EAAE,UAAA,EAAY,yBAA0B,EAAA,GAAIA,2CAAuB,CAAA;AAAA,EACvE,QAAU,EAAA,MAAA;AAAA,EACV,cAAcC,yBAAc,CAAA,OAAA;AAAA,SAC5BC;AACF,CAAC,CAAA;AAGM,MAAM,cAAiB,GAAA;AAGvB,MAAM,oCAAuC,GAAA;AAE7C,MAAM,8BACXC,sDAAkC,CAAA;AAAA,EAChC,WAAA,EAAa,CAACC,8BAAkB,CAAA;AAAA,EAChC,YAAA,EAAc,OAAM,YAAgB,KAAA;AAClC,IAAO,OAAA,YAAA,CAAa,IAAI,CAAe,WAAA,KAAA;AACrC,MAAO,OAAA;AAAA,QACL,UAAY,EAAAA,8BAAA;AAAA,QACZ;AAAA,OACF;AAAA,KACD,CAAA;AAAA,GACH;AAAA,EACA,cAAcH,yBAAc,CAAA,OAAA;AAAA,EAC5B,KAAA,EAAO,MAAO,CAAA,MAAA,CAAOC,WAAK;AAC5B,CAAC;AAEI,MAAM,mBACX,GAAAG,+CAAA,CAA2B,MAAO,CAAA,MAAA,CAAOH,WAAK,CAAC;;;;;;;"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
4
|
-
|
|
5
|
-
var RESOURCE_TYPE = /* @__PURE__ */ ((RESOURCE_TYPE2) => {
|
|
6
|
-
RESOURCE_TYPE2["PROJECT"] = "mend-project";
|
|
7
|
-
return RESOURCE_TYPE2;
|
|
8
|
-
})(RESOURCE_TYPE || {});
|
|
9
|
-
const mendReadPermission = pluginPermissionCommon.createPermission({
|
|
10
|
-
name: "mend.project.read",
|
|
11
|
-
attributes: { action: "read" },
|
|
12
|
-
resourceType: "mend-project" /* PROJECT */
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
exports.RESOURCE_TYPE = RESOURCE_TYPE;
|
|
16
|
-
exports.mendReadPermission = mendReadPermission;
|
|
17
|
-
//# sourceMappingURL=permissions.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.cjs.js","sources":["../../src/permission/permissions.ts"],"sourcesContent":["import { createPermission } from '@backstage/plugin-permission-common';\n\n/** @public */\nexport enum RESOURCE_TYPE {\n PROJECT = 'mend-project',\n}\n\n/** @public */\nexport const mendReadPermission = createPermission({\n name: 'mend.project.read',\n attributes: { action: 'read' },\n resourceType: RESOURCE_TYPE.PROJECT,\n});\n\nexport const mendPermissions = [mendReadPermission];\n"],"names":["RESOURCE_TYPE","createPermission"],"mappings":";;;;AAGY,IAAA,aAAA,qBAAAA,cAAL,KAAA;AACL,EAAAA,eAAA,SAAU,CAAA,GAAA,cAAA;AADA,EAAAA,OAAAA,cAAAA;AAAA,CAAA,EAAA,aAAA,IAAA,EAAA;AAKL,MAAM,qBAAqBC,uCAAiB,CAAA;AAAA,EACjD,IAAM,EAAA,mBAAA;AAAA,EACN,UAAA,EAAY,EAAE,MAAA,EAAQ,MAAO,EAAA;AAAA,EAC7B,YAAc,EAAA,cAAA;AAChB,CAAC;;;;;"}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var zod = require('zod');
|
|
4
|
-
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
|
-
var permissions = require('./permissions.cjs.js');
|
|
6
|
-
|
|
7
|
-
const createProjectPermissionRule = pluginPermissionNode.makeCreatePermissionRule();
|
|
8
|
-
const filter = createProjectPermissionRule({
|
|
9
|
-
name: "filter",
|
|
10
|
-
description: "Should allow read-only access to filtered projects.",
|
|
11
|
-
resourceType: permissions.RESOURCE_TYPE.PROJECT,
|
|
12
|
-
paramsSchema: zod.z.object({
|
|
13
|
-
ids: zod.z.string().array().describe("Project ID to match resource"),
|
|
14
|
-
exclude: zod.z.boolean().optional().describe("Exclude or include project")
|
|
15
|
-
}),
|
|
16
|
-
apply: (resource, { ids, exclude = true }) => {
|
|
17
|
-
return exclude ? !ids.includes(resource.resourceRef) : ids.includes(resource.resourceRef);
|
|
18
|
-
},
|
|
19
|
-
toQuery: ({ ids, exclude = true }) => {
|
|
20
|
-
return {
|
|
21
|
-
ids,
|
|
22
|
-
exclude
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
const rules = { filter };
|
|
27
|
-
|
|
28
|
-
exports.createProjectPermissionRule = createProjectPermissionRule;
|
|
29
|
-
exports.filter = filter;
|
|
30
|
-
exports.rules = rules;
|
|
31
|
-
//# sourceMappingURL=rules.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rules.cjs.js","sources":["../../src/permission/rules.ts"],"sourcesContent":["import { z } from 'zod';\nimport { makeCreatePermissionRule } from '@backstage/plugin-permission-node';\nimport { RESOURCE_TYPE } from './permissions';\n\ntype PermissionAttributes = {\n action?: 'create' | 'read' | 'update' | 'delete';\n};\n\ntype ResourceProps = {\n permission: {\n type: string;\n name: string;\n attributes: PermissionAttributes;\n resourceType: typeof RESOURCE_TYPE.PROJECT;\n };\n resourceRef: string;\n};\n\n/** @public */\nexport type FilterProps = {\n ids: string[];\n exclude?: boolean;\n};\n\nexport const createProjectPermissionRule = makeCreatePermissionRule<\n ResourceProps,\n FilterProps,\n typeof RESOURCE_TYPE.PROJECT\n>();\n\nexport const filter = createProjectPermissionRule({\n name: 'filter',\n description: 'Should allow read-only access to filtered projects.',\n resourceType: RESOURCE_TYPE.PROJECT,\n paramsSchema: z.object({\n ids: z.string().array().describe('Project ID to match resource'),\n exclude: z.boolean().optional().describe('Exclude or include project'),\n }),\n apply: (resource, { ids, exclude = true }) => {\n return exclude\n ? !ids.includes(resource.resourceRef)\n : ids.includes(resource.resourceRef);\n },\n toQuery: ({ ids, exclude = true }) => {\n return {\n ids,\n exclude,\n };\n },\n});\n\nexport const rules = { filter };\n"],"names":["makeCreatePermissionRule","RESOURCE_TYPE","z"],"mappings":";;;;;;AAwBO,MAAM,8BAA8BA,6CAIzC;AAEK,MAAM,SAAS,2BAA4B,CAAA;AAAA,EAChD,IAAM,EAAA,QAAA;AAAA,EACN,WAAa,EAAA,qDAAA;AAAA,EACb,cAAcC,yBAAc,CAAA,OAAA;AAAA,EAC5B,YAAA,EAAcC,MAAE,MAAO,CAAA;AAAA,IACrB,KAAKA,KAAE,CAAA,MAAA,GAAS,KAAM,EAAA,CAAE,SAAS,8BAA8B,CAAA;AAAA,IAC/D,SAASA,KAAE,CAAA,OAAA,GAAU,QAAS,EAAA,CAAE,SAAS,4BAA4B;AAAA,GACtE,CAAA;AAAA,EACD,OAAO,CAAC,QAAA,EAAU,EAAE,GAAK,EAAA,OAAA,GAAU,MAAW,KAAA;AAC5C,IAAO,OAAA,OAAA,GACH,CAAC,GAAA,CAAI,QAAS,CAAA,QAAA,CAAS,WAAW,CAClC,GAAA,GAAA,CAAI,QAAS,CAAA,QAAA,CAAS,WAAW,CAAA;AAAA,GACvC;AAAA,EACA,SAAS,CAAC,EAAE,GAAK,EAAA,OAAA,GAAU,MAAW,KAAA;AACpC,IAAO,OAAA;AAAA,MACL,GAAA;AAAA,MACA;AAAA,KACF;AAAA;AAEJ,CAAC;AAEY,MAAA,KAAA,GAAQ,EAAE,MAAO;;;;;;"}
|