Skip to content

Commit 033a502

Browse files
author
Lukas Fischer
committed
#1838 Add unit tests for dependencytrack hook
Make sure the persistence-dependencytrack hook can be tested automatically by providing some tests for it. Some parts of the tests might be coupled a bit tightly to the actual implementation, if the endpoint is switched to the PUT endpoint the tests will have to be adapted. The tests inject the mocked dependencies through parameters, similar to how the tests for the generic webhook work. Since fetch() is the only dependency here, this works pretty well. Signed-off-by: Lukas Fischer <lukas.fischer@iteratec.com>
1 parent fca9fc0 commit 033a502

2 files changed

Lines changed: 118 additions & 5 deletions

File tree

hooks/persistence-dependencytrack/hook/hook.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44

5-
const apiKey = process.env["DEPENDENCYTRACK_APIKEY"]
6-
const baseUrl = process.env["DEPENDENCYTRACK_URL"];
7-
const url = baseUrl.replace(/\/$/, "") + "/api/v1/bom"
8-
9-
async function handle({ getRawResults, scan }) {
5+
async function handle({
6+
getRawResults,
7+
scan,
8+
apiKey = process.env["DEPENDENCYTRACK_APIKEY"],
9+
baseUrl = process.env["DEPENDENCYTRACK_URL"],
10+
fetch = global.fetch
11+
}) {
1012
if (scan.status.rawResultType !== "sbom-cyclonedx") {
1113
// Not an SBOM scan, cannot be handled by Dependency-Track, ignore
1214
console.log(`Scan ${scan.metadata.name} is not an SBOM scan, ignoring.`);
@@ -39,6 +41,7 @@ async function handle({ getRawResults, scan }) {
3941
formData.append("projectVersion", version);
4042
formData.append("bom", JSON.stringify(result));
4143

44+
const url = baseUrl.replace(/\/$/, "") + "/api/v1/bom"
4245
console.log(`Uploading SBOM for name: ${name} version: ${version} to ${url}`);
4346

4447
// Send request to API endpoint
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// SPDX-FileCopyrightText: the secureCodeBox authors
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
const { handle } = require("./hook");
6+
const fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ token: "statustoken" }) }));
7+
8+
beforeEach(() => {
9+
jest.clearAllMocks();
10+
});
11+
12+
test("should not send a post request if not an SBOM scan", async () => {
13+
const result = {};
14+
15+
const getRawResults = async () => result;
16+
17+
const scan = {
18+
metadata: {
19+
uid: "25ea7ba4-48cf-45e4-ae5c-be1de83df9b8",
20+
name: "demo-trivy",
21+
},
22+
status: {
23+
rawResultType: "trivy-json"
24+
}
25+
};
26+
27+
const apiKey = "verysecretgitleaksplsignore"
28+
const baseUrl = "http://example.com/foo/bar";
29+
30+
await handle({ getRawResults, scan, apiKey, baseUrl, fetch });
31+
32+
expect(fetch).toBeCalledTimes(0);
33+
});
34+
35+
test("should not send a post request if not a CycloneDX SBOM", async () => {
36+
const result = {
37+
spdxVersion: "SPDX-2.3",
38+
dataLicense: "CC0-1.0",
39+
SPDXID: "SPDXRef-DOCUMENT",
40+
name: "bkimminich/juice-shop:v15.0.0",
41+
documentNamespace: "https://anchore.com/syft/image/bkimminich/juice-shop-v15.0.0-f25938fd-9d66-4dc6-a4c6-b0390b4cf037",
42+
creationInfo: {
43+
licenseListVersion: "3.21",
44+
creators: [
45+
"Organization: Anchore, Inc",
46+
"Tool: syft-0.85.0",
47+
],
48+
created: "2023-08-02T11:42:48Z",
49+
}
50+
};
51+
52+
const getRawResults = async () => result;
53+
54+
// technically we're saying here that this scan is a CycloneDX scan even though we're then sending something looking like an SPDX SBOM
55+
const scan = {
56+
metadata: {
57+
uid: "c79e135e-3624-47dc-92d1-2ae6e7355a44",
58+
name: "demo-sbom",
59+
},
60+
status: {
61+
rawResultType: "sbom-cyclonedx"
62+
}
63+
};
64+
65+
const apiKey = "verysecretgitleaksplsignore"
66+
const baseUrl = "http://example.com/foo/bar";
67+
68+
await handle({ getRawResults, scan, apiKey, baseUrl, fetch });
69+
70+
expect(fetch).toBeCalledTimes(0);
71+
});
72+
73+
test("should send a post request to the url when fired", async () => {
74+
const result = {
75+
bomFormat: "CycloneDX",
76+
metadata: {
77+
component: {
78+
name: "hello-world:latest"
79+
}
80+
}
81+
};
82+
83+
const getRawResults = async () => result;
84+
85+
const scan = {
86+
metadata: {
87+
uid: "69e71358-bb01-425b-9bde-e45653605490",
88+
name: "demo-sbom",
89+
},
90+
status: {
91+
rawResultType: "sbom-cyclonedx"
92+
}
93+
};
94+
95+
const apiKey = "verysecretgitleaksplsignore"
96+
const baseUrl = "http://example.com/foo/bar";
97+
const url = baseUrl + "/api/v1/bom"
98+
99+
await handle({ getRawResults, scan, apiKey, baseUrl, fetch });
100+
101+
expect(fetch).toBeCalledTimes(1);
102+
expect(fetch).toBeCalledWith(url, expect.objectContaining({
103+
method: "POST",
104+
headers: {
105+
"X-API-Key": apiKey,
106+
},
107+
}));
108+
109+
expect(fetch.mock.calls[0][1].body.get("bom")).toBe(JSON.stringify(result));
110+
});

0 commit comments

Comments
 (0)