Skip to content
This repository was archived by the owner on Oct 14, 2020. It is now read-only.

Commit 8a7786c

Browse files
committed
#33 Implement Declarative CascadingScans
Migrated to main hook file to typescript
1 parent d8bc027 commit 8a7786c

File tree

7 files changed

+276
-72
lines changed

7 files changed

+276
-72
lines changed
Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1-
# This image doesn't install the hooks dependencies, as it only has the @kubernetes/client-node dependencies which is already installed via the hook-sdk
1+
FROM node:12-alpine as install
2+
RUN mkdir -p /home/app
3+
WORKDIR /home/app
4+
COPY package.json package-lock.json ./
5+
RUN npm ci --production
6+
7+
FROM node:12-alpine as build
8+
RUN mkdir -p /home/app
9+
WORKDIR /home/app
10+
COPY package.json package-lock.json ./
11+
RUN npm ci
12+
COPY hook.ts scan-helpers.js ./
13+
RUN npm run build
214

315
FROM scbexperimental/hook-sdk-nodejs:latest
416
WORKDIR /home/app/hook-wrapper/hook/
5-
COPY --chown=app:app hook.js scan-helpers.js ./
17+
COPY --chown=app:app scan-helpers.js ./
18+
COPY --from=build --chown=app:app /home/app/hook.js ./
19+
COPY --from=install --chown=app:app /home/app/node_modules/ ./node_modules/
Lines changed: 113 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,119 @@
1-
const { startSubsequentSecureCodeBoxScan } = require("./scan-helpers");
2-
const isMatch = require("lodash.ismatch");
3-
4-
async function handle({ scan, getFindings }) {
5-
const findings = await getFindings();
6-
const cascadingRules = await getCascadingRules();
7-
8-
const cascadingScans = getCascadingScans(findings, cascadingRules);
9-
10-
for (const { scanType, parameters } of cascadingScans) {
11-
await startSubsequentSecureCodeBoxScan({
12-
parentScan: scan,
13-
scanType,
14-
parameters,
1+
"use strict";
2+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4+
return new (P || (P = Promise))(function (resolve, reject) {
5+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8+
step((generator = generator.apply(thisArg, _arguments || [])).next());
9+
});
10+
};
11+
var __generator = (this && this.__generator) || function (thisArg, body) {
12+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14+
function verb(n) { return function (v) { return step([n, v]); }; }
15+
function step(op) {
16+
if (f) throw new TypeError("Generator is already executing.");
17+
while (_) try {
18+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19+
if (y = 0, t) op = [op[0] & 2, t.value];
20+
switch (op[0]) {
21+
case 0: case 1: t = op; break;
22+
case 4: _.label++; return { value: op[1], done: false };
23+
case 5: _.label++; y = op[1]; op = [0]; continue;
24+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
25+
default:
26+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30+
if (t[2]) _.ops.pop();
31+
_.trys.pop(); continue;
32+
}
33+
op = body.call(thisArg, _);
34+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36+
}
37+
};
38+
exports.__esModule = true;
39+
exports.getCascadingScans = exports.handle = void 0;
40+
var lodash_1 = require("lodash");
41+
var Mustache = require("mustache");
42+
var scan_helpers_1 = require("./scan-helpers");
43+
function handle(_a) {
44+
var scan = _a.scan, getFindings = _a.getFindings;
45+
return __awaiter(this, void 0, void 0, function () {
46+
var findings, cascadingRules, cascadingScans, _i, cascadingScans_1, _b, name_1, parameters;
47+
return __generator(this, function (_c) {
48+
switch (_c.label) {
49+
case 0: return [4 /*yield*/, getFindings()];
50+
case 1:
51+
findings = _c.sent();
52+
return [4 /*yield*/, getCascadingRules()];
53+
case 2:
54+
cascadingRules = _c.sent();
55+
cascadingScans = getCascadingScans(findings, cascadingRules);
56+
_i = 0, cascadingScans_1 = cascadingScans;
57+
_c.label = 3;
58+
case 3:
59+
if (!(_i < cascadingScans_1.length)) return [3 /*break*/, 6];
60+
_b = cascadingScans_1[_i], name_1 = _b.name, parameters = _b.parameters;
61+
return [4 /*yield*/, scan_helpers_1.startSubsequentSecureCodeBoxScan({
62+
parentScan: scan,
63+
scanType: name_1,
64+
parameters: parameters
65+
})];
66+
case 4:
67+
_c.sent();
68+
_c.label = 5;
69+
case 5:
70+
_i++;
71+
return [3 /*break*/, 3];
72+
case 6: return [2 /*return*/];
73+
}
74+
});
1575
});
16-
}
1776
}
18-
19-
async function getCascadingRules() {
20-
// Todo: Get all CascadingRules of the current Namespace via k8s api
21-
return [];
77+
exports.handle = handle;
78+
function getCascadingRules() {
79+
return __awaiter(this, void 0, void 0, function () {
80+
return __generator(this, function (_a) {
81+
switch (_a.label) {
82+
case 0: return [4 /*yield*/, scan_helpers_1.getCascadingRulesFromCluster()];
83+
case 1:
84+
// Explicit Cast to the proper Type
85+
return [2 /*return*/, _a.sent()];
86+
}
87+
});
88+
});
2289
}
23-
24-
// Todo remove eslint disable
25-
// eslint-disable-next-line no-unused-vars
90+
/**
91+
* Goes thought the Findings and the CascadingRules
92+
* and returns a List of Scans which should be started based on both.
93+
*/
2694
function getCascadingScans(findings, cascadingRules) {
27-
const cascadingScans = [];
28-
29-
for (const cascadingRule of cascadingRules) {
30-
for (const finding of findings) {
31-
const matches = cascadingRule.spec.matches.some((matchesRule) =>
32-
isMatch(finding, matchesRule)
33-
);
34-
35-
if (matches) {
36-
// Todo templating
37-
cascadingScans.push(cascadingRule.spec.scanSpec);
38-
}
95+
var cascadingScans = [];
96+
for (var _i = 0, cascadingRules_1 = cascadingRules; _i < cascadingRules_1.length; _i++) {
97+
var cascadingRule = cascadingRules_1[_i];
98+
var _loop_1 = function (finding) {
99+
var matches = cascadingRule.spec.matches.some(function (matchesRule) {
100+
return lodash_1.isMatch(finding, matchesRule);
101+
});
102+
if (matches) {
103+
var _a = cascadingRule.spec.scanSpec, name_2 = _a.name, parameters = _a.parameters;
104+
cascadingScans.push({
105+
name: Mustache.render(name_2, finding),
106+
parameters: parameters.map(function (parameter) {
107+
return Mustache.render(parameter, finding);
108+
})
109+
});
110+
}
111+
};
112+
for (var _a = 0, findings_1 = findings; _a < findings_1.length; _a++) {
113+
var finding = findings_1[_a];
114+
_loop_1(finding);
115+
}
39116
}
40-
}
41-
42-
return cascadingScans;
117+
return cascadingScans;
43118
}
44-
45-
module.exports.getCascadingScans = getCascadingScans;
46-
module.exports.handle = handle;
119+
exports.getCascadingScans = getCascadingScans;

hooks/declarative-subsequent-scans/hook.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ test("Should create subsequent scans for open HTTPS ports (NMAP findings)", () =
3939
],
4040
scanSpec: {
4141
name: "sslyze",
42-
parameters: ["--regular", "{attributes.hostname}"]
42+
parameters: ["--regular", "{{attributes.hostname}}"]
4343
}
4444
}
4545
}
@@ -53,7 +53,7 @@ test("Should create subsequent scans for open HTTPS ports (NMAP findings)", () =
5353
"name": "sslyze",
5454
"parameters": Array [
5555
"--regular",
56-
"{attributes.hostname}",
56+
"foobar.com",
5757
],
5858
},
5959
]
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { isMatch } from "lodash";
2+
import * as Mustache from "mustache";
3+
import * as k8s from "@kubernetes/client-node";
4+
5+
import {
6+
startSubsequentSecureCodeBoxScan,
7+
getCascadingRulesFromCluster,
8+
} from "./scan-helpers";
9+
10+
interface Finding {
11+
name: string;
12+
location: string;
13+
category: string;
14+
severity: string;
15+
osi_layer: string;
16+
attributes: Map<string, string | number>;
17+
}
18+
19+
interface HandleArgs {
20+
scan: any;
21+
getFindings: () => Array<Finding>;
22+
}
23+
24+
interface CascadingRules {
25+
metadata: k8s.V1ObjectMeta;
26+
spec: CascadingRuleSpec;
27+
}
28+
29+
interface CascadingRuleSpec {
30+
matches: Array<Finding>;
31+
scanSpec: ScanSpec;
32+
}
33+
34+
interface Scan {
35+
metadata: k8s.V1ObjectMeta;
36+
spec: ScanSpec;
37+
}
38+
39+
interface ScanSpec {
40+
name: string;
41+
parameters: Array<string>;
42+
}
43+
44+
export async function handle({ scan, getFindings }: HandleArgs) {
45+
const findings = await getFindings();
46+
const cascadingRules = await getCascadingRules();
47+
48+
const cascadingScans = getCascadingScans(findings, cascadingRules);
49+
50+
for (const { name, parameters } of cascadingScans) {
51+
await startSubsequentSecureCodeBoxScan({
52+
parentScan: scan,
53+
scanType: name,
54+
parameters,
55+
});
56+
}
57+
}
58+
59+
async function getCascadingRules(): Promise<Array<CascadingRules>> {
60+
// Explicit Cast to the proper Type
61+
return <Array<CascadingRules>>await getCascadingRulesFromCluster();
62+
}
63+
64+
/**
65+
* Goes thought the Findings and the CascadingRules
66+
* and returns a List of Scans which should be started based on both.
67+
*/
68+
export function getCascadingScans(
69+
findings: Array<Finding>,
70+
cascadingRules: Array<CascadingRules>
71+
): Array<ScanSpec> {
72+
const cascadingScans: Array<ScanSpec> = [];
73+
74+
for (const cascadingRule of cascadingRules) {
75+
for (const finding of findings) {
76+
const matches = cascadingRule.spec.matches.some((matchesRule) =>
77+
isMatch(finding, matchesRule)
78+
);
79+
80+
if (matches) {
81+
const { name, parameters } = cascadingRule.spec.scanSpec;
82+
83+
cascadingScans.push({
84+
name: Mustache.render(name, finding),
85+
parameters: parameters.map((parameter) =>
86+
Mustache.render(parameter, finding)
87+
),
88+
});
89+
}
90+
}
91+
}
92+
93+
return cascadingScans;
94+
}

hooks/declarative-subsequent-scans/package-lock.json

Lines changed: 11 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hooks/declarative-subsequent-scans/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@
44
"description": "",
55
"main": "hook.js",
66
"scripts": {
7+
"build": "npx typescript hook.ts",
78
"test": "jest ."
89
},
910
"keywords": [],
1011
"author": "",
1112
"license": "Apache-2.0",
1213
"dependencies": {
1314
"@kubernetes/client-node": "^0.12.0",
14-
"lodash.ismatch": "^4.4.0"
15+
"lodash": "^4.17.15",
16+
"mustache": "^4.0.1"
1517
},
1618
"devDependencies": {
17-
"jest": "^25.1.0"
19+
"jest": "^25.1.0",
20+
"typescript": "^3.9.5"
1821
}
1922
}

0 commit comments

Comments
 (0)