-
Notifications
You must be signed in to change notification settings - Fork 179
Expand file tree
/
Copy pathhook-wrapper.js
More file actions
178 lines (159 loc) · 4.97 KB
/
hook-wrapper.js
File metadata and controls
178 lines (159 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// SPDX-FileCopyrightText: the secureCodeBox authors
//
// SPDX-License-Identifier: Apache-2.0
import {
KubeConfig,
CustomObjectsApi,
setHeaderOptions,
PatchStrategy,
} from "@kubernetes/client-node";
import { handle } from "./hook/hook.js";
const kc = new KubeConfig();
kc.loadFromCluster();
const k8sApi = kc.makeApiClient(CustomObjectsApi);
const scanName = process.env["SCAN_NAME"];
const namespace = process.env["NAMESPACE"];
function downloadFile(url) {
return fetch(url);
}
async function getRawResults() {
const rawResultUrl = process.argv[2];
const response = await downloadFile(rawResultUrl);
console.log(`Fetched raw result file contents from the file storage`);
return await response.text();
}
async function getFindings() {
const findingsUrl = process.argv[3];
const response = await downloadFile(findingsUrl);
const findings = await response.json();
console.log(`Fetched ${findings.length} findings from the file storage`);
return findings;
}
async function uploadFile(url, fileContents) {
try {
const response = await fetch(url, {
method: "PUT",
headers: { "content-type": "" },
body: fileContents,
});
if (!response.ok) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
const error = new Error(`HTTP error! status: ${response.status}`);
error.response = response;
throw error;
}
} catch (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.error(
`File Upload Failed with Response Code: ${error.response.status}`,
);
const errorBody = await error.response.text();
console.error(`Error Response Body: ${errorBody}`);
} else {
// Something happened in setting up the request that triggered an Error
console.error("Error uploading findings from hook", error.message);
}
process.exit(1);
}
}
function updateRawResults(fileContents) {
const rawResultUploadUrl = process.argv[4];
if (rawResultUploadUrl === undefined) {
console.error(
"Tried to upload RawResults but didn't find a valid URL to upload the findings to.",
);
console.error("This probably means that this hook is a ReadOnly hook.");
console.error(
"If you want to change RawResults you'll need to use a ReadAndWrite Hook.",
);
}
return uploadFile(rawResultUploadUrl, fileContents);
}
function severityCount(findings, severity) {
return findings.filter(
({ severity: findingSeverity }) =>
findingSeverity.toUpperCase() === severity,
).length;
}
async function updateFindings(findings) {
const findingsUploadUrl = process.argv[5];
if (findingsUploadUrl === undefined) {
console.error(
"Tried to upload Findings but didn't find a valid URL to upload the findings to.",
);
console.error("This probably means that this hook is a ReadOnly hook.");
console.error(
"If you want to change Findings you'll need to use a ReadAndWrite Hook.",
);
}
await uploadFile(findingsUploadUrl, JSON.stringify(findings));
// Update the scans findingStats (severities, categories, or the count) of the scan results
const findingCategories = new Map();
for (const { category } of findings) {
if (findingCategories.has(category)) {
findingCategories.set(category, findingCategories.get(category) + 1);
} else {
findingCategories.set(category, 1);
}
}
await k8sApi.patchNamespacedCustomObjectStatus(
{
group: "execution.securecodebox.io",
version: "v1",
namespace,
plural: "scans",
name: scanName,
body: {
status: {
findings: {
count: findings.length,
severities: {
informational: severityCount(findings, "INFORMATIONAL"),
low: severityCount(findings, "LOW"),
medium: severityCount(findings, "MEDIUM"),
high: severityCount(findings, "HIGH"),
},
categories: Object.fromEntries(findingCategories.entries()),
},
},
},
},
setHeaderOptions("Content-Type", PatchStrategy.MergePatch),
);
console.log("Updated status successfully");
}
async function main() {
console.log(`Starting hook for Scan "${scanName}"`);
let scan;
try {
scan = await k8sApi.getNamespacedCustomObject({
group: "execution.securecodebox.io",
version: "v1",
namespace: namespace,
plural: "scans",
name: scanName,
});
} catch (err) {
console.error("Failed to get Scan from the kubernetes api");
console.error(err);
process.exit(1);
}
try {
await handle({
getRawResults,
getFindings,
updateRawResults,
updateFindings,
scan,
});
} catch (error) {
console.error("Error was thrown while running hooks handle function");
console.error(error);
process.exit(1);
}
console.log(`Hook completed`);
}
main();