@kwirthmagnify/kwirth-plugin-trivy 0.2.5 → 0.2.6
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/back.js +250 -252
- package/front.js +15 -17
- package/package.json +1 -1
package/back.js
CHANGED
|
@@ -68,32 +68,260 @@ var TRIVY_API_AUDIT_PLURAL = "configauditreports";
|
|
|
68
68
|
var TRIVY_API_SBOM_PLURAL = "sbomreports";
|
|
69
69
|
var TRIVY_API_EXPOSED_PLURAL = "exposedsecretreports";
|
|
70
70
|
var TrivyChannel = class {
|
|
71
|
-
channelId = "trivy";
|
|
72
|
-
requirements = { storage: false, providers: [] };
|
|
73
|
-
clusterInfo;
|
|
74
|
-
backChannelObject;
|
|
75
|
-
informers = /* @__PURE__ */ new Map();
|
|
76
|
-
webSockets = [];
|
|
77
71
|
constructor(clusterInfo, backChannelObject) {
|
|
72
|
+
this.channelId = "trivy";
|
|
73
|
+
this.requirements = { storage: false, providers: [] };
|
|
74
|
+
this.informers = /* @__PURE__ */ new Map();
|
|
75
|
+
this.webSockets = [];
|
|
76
|
+
this.getChannelData = () => ({
|
|
77
|
+
id: this.channelId,
|
|
78
|
+
routable: false,
|
|
79
|
+
pauseable: false,
|
|
80
|
+
modifiable: false,
|
|
81
|
+
reconnectable: false,
|
|
82
|
+
metrics: false,
|
|
83
|
+
sources: [import_kwirth_common.EClusterType.KUBERNETES],
|
|
84
|
+
endpoints: [{ name: "operator", methods: ["GET"], requiresAccessKey: true }],
|
|
85
|
+
websocket: false,
|
|
86
|
+
cluster: false,
|
|
87
|
+
resourced: true
|
|
88
|
+
});
|
|
89
|
+
this.getChannelScopeLevel = (scope) => ["", "trivy$workload", "trivy$kubernetes", "cluster"].indexOf(scope);
|
|
90
|
+
this.startChannel = async () => {
|
|
91
|
+
};
|
|
92
|
+
this.containsAsset = (webSocket, podNamespace, podName, containerName) => {
|
|
93
|
+
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
94
|
+
return socket?.instances.some((i) => i.assets.some((a) => a.podNamespace === podNamespace && a.podName === podName && a.containerName === containerName)) ?? false;
|
|
95
|
+
};
|
|
96
|
+
this.containsInstance = (instanceId) => this.webSockets.some((s) => s.instances.find((i) => i.instanceId === instanceId));
|
|
97
|
+
this.processCommand = async (webSocket, instanceMessage) => {
|
|
98
|
+
if (instanceMessage.flow === import_kwirth_common.EInstanceMessageFlow.IMMEDIATE) return false;
|
|
99
|
+
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
100
|
+
if (!socket) return false;
|
|
101
|
+
const instance = socket.instances.find((i) => i.instanceId === instanceMessage.instance);
|
|
102
|
+
if (!instance) {
|
|
103
|
+
this.sendSignalMessage(webSocket, instanceMessage.action, import_kwirth_common.EInstanceMessageFlow.RESPONSE, import_kwirth_common.ESignalMessageLevel.ERROR, instanceMessage.instance, `Instance not found`);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const resp = await this.executeCommand(instanceMessage, instance);
|
|
107
|
+
if (resp) webSocket.send(JSON.stringify(resp));
|
|
108
|
+
return Boolean(resp);
|
|
109
|
+
};
|
|
110
|
+
this.addObject = async (webSocket, instanceConfig, podNamespace, podName, containerName) => {
|
|
111
|
+
let socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
112
|
+
if (!socket) {
|
|
113
|
+
const len = this.webSockets.push({ ws: webSocket, lastRefresh: Date.now(), instances: [] });
|
|
114
|
+
socket = this.webSockets[len - 1];
|
|
115
|
+
}
|
|
116
|
+
let instance = socket.instances.find((i) => i.instanceId === instanceConfig.instance);
|
|
117
|
+
if (!instance) {
|
|
118
|
+
instance = { accessKey: (0, import_kwirth_common.accessKeyDeserialize)(instanceConfig.accessKey), instanceId: instanceConfig.instance, assets: [], maxCritical: 0, maxHigh: 0, maxMedium: 0, maxLow: 0 };
|
|
119
|
+
socket.instances.push(instance);
|
|
120
|
+
}
|
|
121
|
+
const ic = instanceConfig.data;
|
|
122
|
+
if (ic) {
|
|
123
|
+
instance.maxCritical = ic.maxCritical;
|
|
124
|
+
instance.maxHigh = ic.maxHigh;
|
|
125
|
+
instance.maxMedium = ic.maxMedium;
|
|
126
|
+
instance.maxLow = ic.maxLow;
|
|
127
|
+
}
|
|
128
|
+
const asset = { podNamespace, podName, containerName };
|
|
129
|
+
const sendIfKnown = (result) => {
|
|
130
|
+
if (!result.known) return;
|
|
131
|
+
const payload = {
|
|
132
|
+
msgtype: "trivymessageresponse",
|
|
133
|
+
msgsubtype: "add",
|
|
134
|
+
id: "",
|
|
135
|
+
namespace: asset.podNamespace,
|
|
136
|
+
group: "",
|
|
137
|
+
pod: asset.podName,
|
|
138
|
+
container: asset.containerName,
|
|
139
|
+
action: import_kwirth_common.EInstanceMessageAction.NONE,
|
|
140
|
+
flow: import_kwirth_common.EInstanceMessageFlow.UNSOLICITED,
|
|
141
|
+
type: import_kwirth_common.EInstanceMessageType.DATA,
|
|
142
|
+
channel: import_kwirth_common.EInstanceMessageChannel.TRIVY,
|
|
143
|
+
instance: instance.instanceId
|
|
144
|
+
};
|
|
145
|
+
payload.data = result;
|
|
146
|
+
webSocket.send(JSON.stringify(payload));
|
|
147
|
+
};
|
|
148
|
+
sendIfKnown(await this.getAssetVulnReport(instance, asset));
|
|
149
|
+
sendIfKnown(await this.getAssetAuditReport(instance, asset));
|
|
150
|
+
sendIfKnown(await this.getAssetSbomReport(instance, asset));
|
|
151
|
+
sendIfKnown(await this.getAssetExposedReport(instance, asset));
|
|
152
|
+
instance.assets.push(asset);
|
|
153
|
+
for (const plural of [TRIVY_API_VULN_PLURAL, TRIVY_API_AUDIT_PLURAL, TRIVY_API_SBOM_PLURAL, TRIVY_API_EXPOSED_PLURAL]) {
|
|
154
|
+
if (!this.informers.has(plural)) {
|
|
155
|
+
const informer = this.createInformer(webSocket, instance, plural);
|
|
156
|
+
this.informers.set(plural, informer);
|
|
157
|
+
informer.start();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
};
|
|
162
|
+
this.deleteObject = async (webSocket, instanceConfig, podNamespace, podName, containerName) => {
|
|
163
|
+
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
164
|
+
const instance = socket?.instances.find((i) => i.instanceId === instanceConfig.instance);
|
|
165
|
+
if (instance) instance.assets = instance.assets.filter((a) => !(a.podNamespace === podNamespace && a.podName === podName && (containerName === "" || a.containerName === containerName)));
|
|
166
|
+
return true;
|
|
167
|
+
};
|
|
168
|
+
this.stopInstance = (webSocket, instanceConfig) => {
|
|
169
|
+
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
170
|
+
if (socket?.instances.find((i) => i.instanceId === instanceConfig.instance)) {
|
|
171
|
+
this.removeInstance(webSocket, instanceConfig.instance);
|
|
172
|
+
this.sendSignalMessage(webSocket, import_kwirth_common.EInstanceMessageAction.STOP, import_kwirth_common.EInstanceMessageFlow.RESPONSE, import_kwirth_common.ESignalMessageLevel.INFO, instanceConfig.instance, "Trivy instance stopped");
|
|
173
|
+
} else {
|
|
174
|
+
this.sendSignalMessage(webSocket, import_kwirth_common.EInstanceMessageAction.STOP, import_kwirth_common.EInstanceMessageFlow.RESPONSE, import_kwirth_common.ESignalMessageLevel.ERROR, instanceConfig.instance, `Trivy instance not found`);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
this.removeInstance = (webSocket, instanceId) => {
|
|
178
|
+
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
179
|
+
if (socket) {
|
|
180
|
+
const pos = socket.instances.findIndex((t) => t.instanceId === instanceId);
|
|
181
|
+
if (pos >= 0) socket.instances.splice(pos, 1);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
this.containsConnection = (webSocket) => Boolean(this.webSockets.find((s) => s.ws === webSocket));
|
|
185
|
+
this.removeConnection = (webSocket) => {
|
|
186
|
+
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
187
|
+
if (socket) {
|
|
188
|
+
for (const id of socket.instances.map((i) => i.instanceId)) this.removeInstance(webSocket, id);
|
|
189
|
+
const pos = this.webSockets.findIndex((s) => s.ws === webSocket);
|
|
190
|
+
this.webSockets.splice(pos, 1);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
this.refreshConnection = (webSocket) => {
|
|
194
|
+
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
195
|
+
if (socket) {
|
|
196
|
+
socket.lastRefresh = Date.now();
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
};
|
|
201
|
+
this.updateConnection = (_newWebSocket, _instanceId) => false;
|
|
202
|
+
// ─── PRIVATE ────────────────────────────────────────────────────────────────
|
|
203
|
+
this.sendSignalMessage = (ws, action, flow, level, instanceId, text) => {
|
|
204
|
+
ws.send(JSON.stringify({ action, flow, channel: import_kwirth_common.EInstanceMessageChannel.TRIVY, instance: instanceId, type: import_kwirth_common.EInstanceMessageType.SIGNAL, text, level }));
|
|
205
|
+
};
|
|
206
|
+
this.checkScopes = (instance, scope) => {
|
|
207
|
+
const resources = (0, import_kwirth_common.parseResources)(instance.accessKey.resources);
|
|
208
|
+
const requiredLevel = this.getChannelScopeLevel(scope);
|
|
209
|
+
return resources.some((r) => r.scopes.split(",").some((sc) => this.getChannelScopeLevel(sc) >= requiredLevel));
|
|
210
|
+
};
|
|
211
|
+
this.createInformer = (webSocket, instance, plural) => {
|
|
212
|
+
const handlers = {
|
|
213
|
+
onAdd: (obj) => this.processInformerEvent(webSocket, instance, plural, "add", obj),
|
|
214
|
+
onUpdate: (obj) => this.processInformerEvent(webSocket, instance, plural, "update", obj),
|
|
215
|
+
onDelete: (obj) => this.processInformerEvent(webSocket, instance, plural, "delete", obj),
|
|
216
|
+
onError: (err) => {
|
|
217
|
+
try {
|
|
218
|
+
console.error("[trivy] Informer error:", err);
|
|
219
|
+
if (err["HTTP-Code"] === "404" || err.statusCode === 404)
|
|
220
|
+
console.log("[trivy] CRD not found, informer will not restart");
|
|
221
|
+
else
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
informer.start();
|
|
224
|
+
console.log("[trivy] Informer restarted");
|
|
225
|
+
}, 5e3);
|
|
226
|
+
} catch (e) {
|
|
227
|
+
console.error("[trivy] Error managing informer error:", e);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
const informer = (0, import_kwirth_common_back.createCrdInformer)(this.clusterInfo, TRIVY_API_GROUP, TRIVY_API_VERSION, plural, handlers);
|
|
232
|
+
return informer;
|
|
233
|
+
};
|
|
234
|
+
this.getAssetVulnReport = (instance, asset) => this.getReport(TRIVY_API_VULN_PLURAL, instance, asset, true);
|
|
235
|
+
this.getAssetAuditReport = (instance, asset) => this.getReport(TRIVY_API_AUDIT_PLURAL, instance, asset, false);
|
|
236
|
+
this.getAssetSbomReport = (instance, asset) => this.getReport(TRIVY_API_SBOM_PLURAL, instance, asset, true);
|
|
237
|
+
this.getAssetExposedReport = (instance, asset) => this.getReport(TRIVY_API_EXPOSED_PLURAL, instance, asset, true);
|
|
238
|
+
this.removeReport = async (plural, trivyMessage) => {
|
|
239
|
+
const crdName = await this.getCrdName(trivyMessage.namespace, trivyMessage.pod, trivyMessage.container);
|
|
240
|
+
if (crdName) {
|
|
241
|
+
try {
|
|
242
|
+
await this.clusterInfo.crdApi.deleteNamespacedCustomObject({ group: TRIVY_API_GROUP, version: TRIVY_API_VERSION, namespace: trivyMessage.namespace, plural, name: crdName });
|
|
243
|
+
return void 0;
|
|
244
|
+
} catch (err) {
|
|
245
|
+
return `Error removing ${plural}: ` + err;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return `Couldn't get CRD name`;
|
|
249
|
+
};
|
|
250
|
+
this.executeCommand = async (trivyMessage, instance) => {
|
|
251
|
+
const resp = {
|
|
252
|
+
msgtype: "trivymessageresponse",
|
|
253
|
+
id: "",
|
|
254
|
+
namespace: trivyMessage.namespace,
|
|
255
|
+
group: trivyMessage.group,
|
|
256
|
+
pod: trivyMessage.pod,
|
|
257
|
+
container: trivyMessage.container,
|
|
258
|
+
action: trivyMessage.action,
|
|
259
|
+
flow: import_kwirth_common.EInstanceMessageFlow.RESPONSE,
|
|
260
|
+
type: import_kwirth_common.EInstanceMessageType.DATA,
|
|
261
|
+
channel: trivyMessage.channel,
|
|
262
|
+
instance: trivyMessage.instance
|
|
263
|
+
};
|
|
264
|
+
if (trivyMessage.command === "rescan" /* RESCAN */) {
|
|
265
|
+
const errors = await Promise.all([TRIVY_API_VULN_PLURAL, TRIVY_API_AUDIT_PLURAL, TRIVY_API_EXPOSED_PLURAL, TRIVY_API_SBOM_PLURAL].map((p) => this.removeReport(p, trivyMessage)));
|
|
266
|
+
const err = errors.find(Boolean);
|
|
267
|
+
if (err) resp.data = err;
|
|
268
|
+
}
|
|
269
|
+
return resp;
|
|
270
|
+
};
|
|
271
|
+
this.processInformerEvent = async (webSocket, instance, plural, event, obj) => {
|
|
272
|
+
const asset = instance.assets.find(
|
|
273
|
+
(a) => "Pod" === obj.metadata.labels["trivy-operator.resource.kind"] && a.containerName === obj.metadata.labels["trivy-operator.container.name"] && a.podNamespace === obj.metadata.labels["trivy-operator.resource.namespace"] && a.podName.startsWith(obj.metadata.labels["trivy-operator.resource.name"])
|
|
274
|
+
);
|
|
275
|
+
if (!asset) return;
|
|
276
|
+
const payload = {
|
|
277
|
+
msgtype: "trivymessageresponse",
|
|
278
|
+
msgsubtype: event,
|
|
279
|
+
id: "",
|
|
280
|
+
namespace: asset.podNamespace,
|
|
281
|
+
group: "",
|
|
282
|
+
pod: asset.podName,
|
|
283
|
+
container: asset.containerName,
|
|
284
|
+
action: import_kwirth_common.EInstanceMessageAction.NONE,
|
|
285
|
+
flow: import_kwirth_common.EInstanceMessageFlow.UNSOLICITED,
|
|
286
|
+
type: import_kwirth_common.EInstanceMessageType.DATA,
|
|
287
|
+
channel: import_kwirth_common.EInstanceMessageChannel.TRIVY,
|
|
288
|
+
instance: instance.instanceId
|
|
289
|
+
};
|
|
290
|
+
if (event === "add" || event === "update") {
|
|
291
|
+
switch (plural) {
|
|
292
|
+
case TRIVY_API_VULN_PLURAL:
|
|
293
|
+
payload.data = await this.getAssetVulnReport(instance, asset);
|
|
294
|
+
break;
|
|
295
|
+
case TRIVY_API_AUDIT_PLURAL:
|
|
296
|
+
payload.data = await this.getAssetAuditReport(instance, asset);
|
|
297
|
+
break;
|
|
298
|
+
case TRIVY_API_SBOM_PLURAL:
|
|
299
|
+
payload.data = await this.getAssetSbomReport(instance, asset);
|
|
300
|
+
break;
|
|
301
|
+
case TRIVY_API_EXPOSED_PLURAL:
|
|
302
|
+
payload.data = await this.getAssetExposedReport(instance, asset);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
payload.data = { known: { name: asset.podName, namespace: asset.podNamespace, container: asset.containerName, report: void 0 } };
|
|
307
|
+
}
|
|
308
|
+
payload.data.resource = plural;
|
|
309
|
+
webSocket.send(JSON.stringify(payload));
|
|
310
|
+
};
|
|
311
|
+
this.getCrdName = async (namespace, podName, containerName) => {
|
|
312
|
+
try {
|
|
313
|
+
const podData = await this.clusterInfo.coreApi.readNamespacedPod({ name: podName, namespace });
|
|
314
|
+
const ctrl = podData.metadata?.ownerReferences?.find((or) => or.controller);
|
|
315
|
+
if (ctrl) return `${ctrl.kind.toLowerCase()}-${ctrl.name}${containerName ? "-" + containerName : ""}`;
|
|
316
|
+
return `pod-${podName}${containerName ? "-" + containerName : ""}`;
|
|
317
|
+
} catch (err) {
|
|
318
|
+
console.error("[trivy] Cannot get CRD name:", err);
|
|
319
|
+
return void 0;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
78
322
|
this.clusterInfo = clusterInfo;
|
|
79
323
|
this.backChannelObject = backChannelObject;
|
|
80
324
|
}
|
|
81
|
-
getChannelData = () => ({
|
|
82
|
-
id: this.channelId,
|
|
83
|
-
routable: false,
|
|
84
|
-
pauseable: false,
|
|
85
|
-
modifiable: false,
|
|
86
|
-
reconnectable: false,
|
|
87
|
-
metrics: false,
|
|
88
|
-
sources: [import_kwirth_common.EClusterType.KUBERNETES],
|
|
89
|
-
endpoints: [{ name: "operator", methods: ["GET"], requiresAccessKey: true }],
|
|
90
|
-
websocket: false,
|
|
91
|
-
cluster: false,
|
|
92
|
-
resourced: true
|
|
93
|
-
});
|
|
94
|
-
getChannelScopeLevel = (scope) => ["", "trivy$workload", "trivy$kubernetes", "cluster"].indexOf(scope);
|
|
95
|
-
startChannel = async () => {
|
|
96
|
-
};
|
|
97
325
|
processProviderEvent(_providerId, _obj) {
|
|
98
326
|
}
|
|
99
327
|
async endpointRequest(endpoint, req, res) {
|
|
@@ -134,152 +362,10 @@ var TrivyChannel = class {
|
|
|
134
362
|
}
|
|
135
363
|
async websocketRequest(_newWebSocket) {
|
|
136
364
|
}
|
|
137
|
-
containsAsset = (webSocket, podNamespace, podName, containerName) => {
|
|
138
|
-
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
139
|
-
return socket?.instances.some((i) => i.assets.some((a) => a.podNamespace === podNamespace && a.podName === podName && a.containerName === containerName)) ?? false;
|
|
140
|
-
};
|
|
141
|
-
containsInstance = (instanceId) => this.webSockets.some((s) => s.instances.find((i) => i.instanceId === instanceId));
|
|
142
|
-
processCommand = async (webSocket, instanceMessage) => {
|
|
143
|
-
if (instanceMessage.flow === import_kwirth_common.EInstanceMessageFlow.IMMEDIATE) return false;
|
|
144
|
-
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
145
|
-
if (!socket) return false;
|
|
146
|
-
const instance = socket.instances.find((i) => i.instanceId === instanceMessage.instance);
|
|
147
|
-
if (!instance) {
|
|
148
|
-
this.sendSignalMessage(webSocket, instanceMessage.action, import_kwirth_common.EInstanceMessageFlow.RESPONSE, import_kwirth_common.ESignalMessageLevel.ERROR, instanceMessage.instance, `Instance not found`);
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
const resp = await this.executeCommand(instanceMessage, instance);
|
|
152
|
-
if (resp) webSocket.send(JSON.stringify(resp));
|
|
153
|
-
return Boolean(resp);
|
|
154
|
-
};
|
|
155
|
-
addObject = async (webSocket, instanceConfig, podNamespace, podName, containerName) => {
|
|
156
|
-
let socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
157
|
-
if (!socket) {
|
|
158
|
-
const len = this.webSockets.push({ ws: webSocket, lastRefresh: Date.now(), instances: [] });
|
|
159
|
-
socket = this.webSockets[len - 1];
|
|
160
|
-
}
|
|
161
|
-
let instance = socket.instances.find((i) => i.instanceId === instanceConfig.instance);
|
|
162
|
-
if (!instance) {
|
|
163
|
-
instance = { accessKey: (0, import_kwirth_common.accessKeyDeserialize)(instanceConfig.accessKey), instanceId: instanceConfig.instance, assets: [], maxCritical: 0, maxHigh: 0, maxMedium: 0, maxLow: 0 };
|
|
164
|
-
socket.instances.push(instance);
|
|
165
|
-
}
|
|
166
|
-
const ic = instanceConfig.data;
|
|
167
|
-
if (ic) {
|
|
168
|
-
instance.maxCritical = ic.maxCritical;
|
|
169
|
-
instance.maxHigh = ic.maxHigh;
|
|
170
|
-
instance.maxMedium = ic.maxMedium;
|
|
171
|
-
instance.maxLow = ic.maxLow;
|
|
172
|
-
}
|
|
173
|
-
const asset = { podNamespace, podName, containerName };
|
|
174
|
-
const sendIfKnown = (result) => {
|
|
175
|
-
if (!result.known) return;
|
|
176
|
-
const payload = {
|
|
177
|
-
msgtype: "trivymessageresponse",
|
|
178
|
-
msgsubtype: "add",
|
|
179
|
-
id: "",
|
|
180
|
-
namespace: asset.podNamespace,
|
|
181
|
-
group: "",
|
|
182
|
-
pod: asset.podName,
|
|
183
|
-
container: asset.containerName,
|
|
184
|
-
action: import_kwirth_common.EInstanceMessageAction.NONE,
|
|
185
|
-
flow: import_kwirth_common.EInstanceMessageFlow.UNSOLICITED,
|
|
186
|
-
type: import_kwirth_common.EInstanceMessageType.DATA,
|
|
187
|
-
channel: import_kwirth_common.EInstanceMessageChannel.TRIVY,
|
|
188
|
-
instance: instance.instanceId
|
|
189
|
-
};
|
|
190
|
-
payload.data = result;
|
|
191
|
-
webSocket.send(JSON.stringify(payload));
|
|
192
|
-
};
|
|
193
|
-
sendIfKnown(await this.getAssetVulnReport(instance, asset));
|
|
194
|
-
sendIfKnown(await this.getAssetAuditReport(instance, asset));
|
|
195
|
-
sendIfKnown(await this.getAssetSbomReport(instance, asset));
|
|
196
|
-
sendIfKnown(await this.getAssetExposedReport(instance, asset));
|
|
197
|
-
instance.assets.push(asset);
|
|
198
|
-
for (const plural of [TRIVY_API_VULN_PLURAL, TRIVY_API_AUDIT_PLURAL, TRIVY_API_SBOM_PLURAL, TRIVY_API_EXPOSED_PLURAL]) {
|
|
199
|
-
if (!this.informers.has(plural)) {
|
|
200
|
-
const informer = this.createInformer(webSocket, instance, plural);
|
|
201
|
-
this.informers.set(plural, informer);
|
|
202
|
-
informer.start();
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return true;
|
|
206
|
-
};
|
|
207
|
-
deleteObject = async (webSocket, instanceConfig, podNamespace, podName, containerName) => {
|
|
208
|
-
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
209
|
-
const instance = socket?.instances.find((i) => i.instanceId === instanceConfig.instance);
|
|
210
|
-
if (instance) instance.assets = instance.assets.filter((a) => !(a.podNamespace === podNamespace && a.podName === podName && (containerName === "" || a.containerName === containerName)));
|
|
211
|
-
return true;
|
|
212
|
-
};
|
|
213
365
|
pauseContinueInstance(_webSocket, _instanceConfig, _action) {
|
|
214
366
|
}
|
|
215
367
|
modifyInstance(_webSocket, _instanceConfig) {
|
|
216
368
|
}
|
|
217
|
-
stopInstance = (webSocket, instanceConfig) => {
|
|
218
|
-
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
219
|
-
if (socket?.instances.find((i) => i.instanceId === instanceConfig.instance)) {
|
|
220
|
-
this.removeInstance(webSocket, instanceConfig.instance);
|
|
221
|
-
this.sendSignalMessage(webSocket, import_kwirth_common.EInstanceMessageAction.STOP, import_kwirth_common.EInstanceMessageFlow.RESPONSE, import_kwirth_common.ESignalMessageLevel.INFO, instanceConfig.instance, "Trivy instance stopped");
|
|
222
|
-
} else {
|
|
223
|
-
this.sendSignalMessage(webSocket, import_kwirth_common.EInstanceMessageAction.STOP, import_kwirth_common.EInstanceMessageFlow.RESPONSE, import_kwirth_common.ESignalMessageLevel.ERROR, instanceConfig.instance, `Trivy instance not found`);
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
removeInstance = (webSocket, instanceId) => {
|
|
227
|
-
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
228
|
-
if (socket) {
|
|
229
|
-
const pos = socket.instances.findIndex((t) => t.instanceId === instanceId);
|
|
230
|
-
if (pos >= 0) socket.instances.splice(pos, 1);
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
containsConnection = (webSocket) => Boolean(this.webSockets.find((s) => s.ws === webSocket));
|
|
234
|
-
removeConnection = (webSocket) => {
|
|
235
|
-
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
236
|
-
if (socket) {
|
|
237
|
-
for (const id of socket.instances.map((i) => i.instanceId)) this.removeInstance(webSocket, id);
|
|
238
|
-
const pos = this.webSockets.findIndex((s) => s.ws === webSocket);
|
|
239
|
-
this.webSockets.splice(pos, 1);
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
refreshConnection = (webSocket) => {
|
|
243
|
-
const socket = this.webSockets.find((s) => s.ws === webSocket);
|
|
244
|
-
if (socket) {
|
|
245
|
-
socket.lastRefresh = Date.now();
|
|
246
|
-
return true;
|
|
247
|
-
}
|
|
248
|
-
return false;
|
|
249
|
-
};
|
|
250
|
-
updateConnection = (_newWebSocket, _instanceId) => false;
|
|
251
|
-
// ─── PRIVATE ────────────────────────────────────────────────────────────────
|
|
252
|
-
sendSignalMessage = (ws, action, flow, level, instanceId, text) => {
|
|
253
|
-
ws.send(JSON.stringify({ action, flow, channel: import_kwirth_common.EInstanceMessageChannel.TRIVY, instance: instanceId, type: import_kwirth_common.EInstanceMessageType.SIGNAL, text, level }));
|
|
254
|
-
};
|
|
255
|
-
checkScopes = (instance, scope) => {
|
|
256
|
-
const resources = (0, import_kwirth_common.parseResources)(instance.accessKey.resources);
|
|
257
|
-
const requiredLevel = this.getChannelScopeLevel(scope);
|
|
258
|
-
return resources.some((r) => r.scopes.split(",").some((sc) => this.getChannelScopeLevel(sc) >= requiredLevel));
|
|
259
|
-
};
|
|
260
|
-
createInformer = (webSocket, instance, plural) => {
|
|
261
|
-
const handlers = {
|
|
262
|
-
onAdd: (obj) => this.processInformerEvent(webSocket, instance, plural, "add", obj),
|
|
263
|
-
onUpdate: (obj) => this.processInformerEvent(webSocket, instance, plural, "update", obj),
|
|
264
|
-
onDelete: (obj) => this.processInformerEvent(webSocket, instance, plural, "delete", obj),
|
|
265
|
-
onError: (err) => {
|
|
266
|
-
try {
|
|
267
|
-
console.error("[trivy] Informer error:", err);
|
|
268
|
-
if (err["HTTP-Code"] === "404" || err.statusCode === 404)
|
|
269
|
-
console.log("[trivy] CRD not found, informer will not restart");
|
|
270
|
-
else
|
|
271
|
-
setTimeout(() => {
|
|
272
|
-
informer.start();
|
|
273
|
-
console.log("[trivy] Informer restarted");
|
|
274
|
-
}, 5e3);
|
|
275
|
-
} catch (e) {
|
|
276
|
-
console.error("[trivy] Error managing informer error:", e);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
const informer = (0, import_kwirth_common_back.createCrdInformer)(this.clusterInfo, TRIVY_API_GROUP, TRIVY_API_VERSION, plural, handlers);
|
|
281
|
-
return informer;
|
|
282
|
-
};
|
|
283
369
|
async getReport(plural, instance, asset, withContainer) {
|
|
284
370
|
try {
|
|
285
371
|
const crdName = await this.getCrdName(asset.podNamespace, asset.podName, withContainer ? asset.containerName : void 0);
|
|
@@ -297,94 +383,6 @@ var TrivyChannel = class {
|
|
|
297
383
|
return { resource: plural, unknown: { container: asset.containerName, name: asset.podName, namespace: asset.podNamespace, statusCode: 999, statusMessage: err } };
|
|
298
384
|
}
|
|
299
385
|
}
|
|
300
|
-
getAssetVulnReport = (instance, asset) => this.getReport(TRIVY_API_VULN_PLURAL, instance, asset, true);
|
|
301
|
-
getAssetAuditReport = (instance, asset) => this.getReport(TRIVY_API_AUDIT_PLURAL, instance, asset, false);
|
|
302
|
-
getAssetSbomReport = (instance, asset) => this.getReport(TRIVY_API_SBOM_PLURAL, instance, asset, true);
|
|
303
|
-
getAssetExposedReport = (instance, asset) => this.getReport(TRIVY_API_EXPOSED_PLURAL, instance, asset, true);
|
|
304
|
-
removeReport = async (plural, trivyMessage) => {
|
|
305
|
-
const crdName = await this.getCrdName(trivyMessage.namespace, trivyMessage.pod, trivyMessage.container);
|
|
306
|
-
if (crdName) {
|
|
307
|
-
try {
|
|
308
|
-
await this.clusterInfo.crdApi.deleteNamespacedCustomObject({ group: TRIVY_API_GROUP, version: TRIVY_API_VERSION, namespace: trivyMessage.namespace, plural, name: crdName });
|
|
309
|
-
return void 0;
|
|
310
|
-
} catch (err) {
|
|
311
|
-
return `Error removing ${plural}: ` + err;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return `Couldn't get CRD name`;
|
|
315
|
-
};
|
|
316
|
-
executeCommand = async (trivyMessage, instance) => {
|
|
317
|
-
const resp = {
|
|
318
|
-
msgtype: "trivymessageresponse",
|
|
319
|
-
id: "",
|
|
320
|
-
namespace: trivyMessage.namespace,
|
|
321
|
-
group: trivyMessage.group,
|
|
322
|
-
pod: trivyMessage.pod,
|
|
323
|
-
container: trivyMessage.container,
|
|
324
|
-
action: trivyMessage.action,
|
|
325
|
-
flow: import_kwirth_common.EInstanceMessageFlow.RESPONSE,
|
|
326
|
-
type: import_kwirth_common.EInstanceMessageType.DATA,
|
|
327
|
-
channel: trivyMessage.channel,
|
|
328
|
-
instance: trivyMessage.instance
|
|
329
|
-
};
|
|
330
|
-
if (trivyMessage.command === "rescan" /* RESCAN */) {
|
|
331
|
-
const errors = await Promise.all([TRIVY_API_VULN_PLURAL, TRIVY_API_AUDIT_PLURAL, TRIVY_API_EXPOSED_PLURAL, TRIVY_API_SBOM_PLURAL].map((p) => this.removeReport(p, trivyMessage)));
|
|
332
|
-
const err = errors.find(Boolean);
|
|
333
|
-
if (err) resp.data = err;
|
|
334
|
-
}
|
|
335
|
-
return resp;
|
|
336
|
-
};
|
|
337
|
-
processInformerEvent = async (webSocket, instance, plural, event, obj) => {
|
|
338
|
-
const asset = instance.assets.find(
|
|
339
|
-
(a) => "Pod" === obj.metadata.labels["trivy-operator.resource.kind"] && a.containerName === obj.metadata.labels["trivy-operator.container.name"] && a.podNamespace === obj.metadata.labels["trivy-operator.resource.namespace"] && a.podName.startsWith(obj.metadata.labels["trivy-operator.resource.name"])
|
|
340
|
-
);
|
|
341
|
-
if (!asset) return;
|
|
342
|
-
const payload = {
|
|
343
|
-
msgtype: "trivymessageresponse",
|
|
344
|
-
msgsubtype: event,
|
|
345
|
-
id: "",
|
|
346
|
-
namespace: asset.podNamespace,
|
|
347
|
-
group: "",
|
|
348
|
-
pod: asset.podName,
|
|
349
|
-
container: asset.containerName,
|
|
350
|
-
action: import_kwirth_common.EInstanceMessageAction.NONE,
|
|
351
|
-
flow: import_kwirth_common.EInstanceMessageFlow.UNSOLICITED,
|
|
352
|
-
type: import_kwirth_common.EInstanceMessageType.DATA,
|
|
353
|
-
channel: import_kwirth_common.EInstanceMessageChannel.TRIVY,
|
|
354
|
-
instance: instance.instanceId
|
|
355
|
-
};
|
|
356
|
-
if (event === "add" || event === "update") {
|
|
357
|
-
switch (plural) {
|
|
358
|
-
case TRIVY_API_VULN_PLURAL:
|
|
359
|
-
payload.data = await this.getAssetVulnReport(instance, asset);
|
|
360
|
-
break;
|
|
361
|
-
case TRIVY_API_AUDIT_PLURAL:
|
|
362
|
-
payload.data = await this.getAssetAuditReport(instance, asset);
|
|
363
|
-
break;
|
|
364
|
-
case TRIVY_API_SBOM_PLURAL:
|
|
365
|
-
payload.data = await this.getAssetSbomReport(instance, asset);
|
|
366
|
-
break;
|
|
367
|
-
case TRIVY_API_EXPOSED_PLURAL:
|
|
368
|
-
payload.data = await this.getAssetExposedReport(instance, asset);
|
|
369
|
-
break;
|
|
370
|
-
}
|
|
371
|
-
} else {
|
|
372
|
-
payload.data = { known: { name: asset.podName, namespace: asset.podNamespace, container: asset.containerName, report: void 0 } };
|
|
373
|
-
}
|
|
374
|
-
payload.data.resource = plural;
|
|
375
|
-
webSocket.send(JSON.stringify(payload));
|
|
376
|
-
};
|
|
377
|
-
getCrdName = async (namespace, podName, containerName) => {
|
|
378
|
-
try {
|
|
379
|
-
const podData = await this.clusterInfo.coreApi.readNamespacedPod({ name: podName, namespace });
|
|
380
|
-
const ctrl = podData.metadata?.ownerReferences?.find((or) => or.controller);
|
|
381
|
-
if (ctrl) return `${ctrl.kind.toLowerCase()}-${ctrl.name}${containerName ? "-" + containerName : ""}`;
|
|
382
|
-
return `pod-${podName}${containerName ? "-" + containerName : ""}`;
|
|
383
|
-
} catch (err) {
|
|
384
|
-
console.error("[trivy] Cannot get CRD name:", err);
|
|
385
|
-
return void 0;
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
386
|
};
|
|
389
387
|
// Annotate the CommonJS export names for ESM import in node:
|
|
390
388
|
0 && (module.exports = {
|
package/front.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
8
|
var __commonJS = (cb, mod) => function __require() {
|
|
10
9
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
10
|
};
|
|
@@ -25,7 +24,6 @@
|
|
|
25
24
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
25
|
mod
|
|
27
26
|
));
|
|
28
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
29
27
|
|
|
30
28
|
// kwirth-globals:@kwirthmagnify/kwirth-common-front
|
|
31
29
|
var require_kwirth_common_front = __commonJS({
|
|
@@ -90,10 +88,10 @@
|
|
|
90
88
|
};
|
|
91
89
|
var TrivyInstanceConfig = class {
|
|
92
90
|
constructor() {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
this.ignoreCritical = false;
|
|
92
|
+
this.ignoreHigh = false;
|
|
93
|
+
this.ignoreMedium = false;
|
|
94
|
+
this.ignoreLow = true;
|
|
97
95
|
}
|
|
98
96
|
};
|
|
99
97
|
|
|
@@ -170,11 +168,11 @@
|
|
|
170
168
|
var TRIVY_API_EXPOSED_PLURAL = "exposedsecretreports";
|
|
171
169
|
var TrivyData = class {
|
|
172
170
|
constructor() {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
this.mode = "card";
|
|
172
|
+
this.started = false;
|
|
173
|
+
this.paused = false;
|
|
174
|
+
this.assets = [];
|
|
175
|
+
this.ri = void 0;
|
|
178
176
|
}
|
|
179
177
|
};
|
|
180
178
|
|
|
@@ -538,11 +536,11 @@
|
|
|
538
536
|
// src/front/TrivyChannel.ts
|
|
539
537
|
var TrivyChannel = class {
|
|
540
538
|
constructor() {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
539
|
+
this.setupVisible = false;
|
|
540
|
+
this.SetupDialog = TrivySetup;
|
|
541
|
+
this.TabContent = TrivyTabContent;
|
|
542
|
+
this.channelId = "trivy";
|
|
543
|
+
this.requirements = {
|
|
546
544
|
accessString: true,
|
|
547
545
|
clusterUrl: true,
|
|
548
546
|
clusterInfo: false,
|
|
@@ -557,7 +555,7 @@
|
|
|
557
555
|
userSettings: false,
|
|
558
556
|
webSocket: true,
|
|
559
557
|
backChannels: false
|
|
560
|
-
}
|
|
558
|
+
};
|
|
561
559
|
}
|
|
562
560
|
getScope() {
|
|
563
561
|
return "trivy$workload";
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "trivy",
|
|
3
3
|
"name": "@kwirthmagnify/kwirth-plugin-trivy",
|
|
4
4
|
"displayName": "Trivy",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.6",
|
|
6
6
|
"description": "Trivy security scanning channel plugin for Kwirth",
|
|
7
7
|
"icon": "VerifiedUser",
|
|
8
8
|
"website": "https://kwirthmagnify.dev"
|