Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d3afc75
feat: Dependency track tags reporting
Oct 13, 2025
89c2627
test(cli): Started implementing unit test for SBOM reporting params
gschafra Oct 22, 2025
ee85a6f
Merge branch 'master' into dependency-track-tags-support
gschafra Oct 22, 2025
068be9a
docs(cli): Docs concerning tag feature on SBOM reporting
gschafra Oct 22, 2025
c3fc454
test(cli): First SBOM reporting test
gschafra Oct 22, 2025
10c6f7c
build(deps): Package fixed versions and overrides
gschafra Oct 22, 2025
9777208
build(deps): Package overrides
gschafra Oct 22, 2025
ef52e56
test(cli): Added another test case
gschafra Oct 22, 2025
a99782e
Merge branch 'master' into dependency-track-tags-support
gschafra Oct 23, 2025
d6e797e
style(cli): Biome fix/format
Oct 23, 2025
df3d26b
build(deps): Reverted overrides, only keeping direct packages
Oct 23, 2025
09a2613
test(utils): Adapted parsePnpmLock test
Oct 23, 2025
24b69ca
style: Biome fix/format
Oct 23, 2025
e58f46d
Merge branch 'master' into dependency-track-tags-support
gschafra Oct 24, 2025
a4efbe0
Merge remote-tracking branch 'origin/dependency-track-tags-support' i…
gschafra Oct 24, 2025
a0661b6
test(cli): Code style
gschafra Oct 24, 2025
a926aa6
Merge branch 'master' into dependency-track-tags-support
gschafra Nov 3, 2025
5844f04
Merge branch 'master' into dependency-track-tags-support
gschafra Nov 7, 2025
41b5824
Merge branch 'master' into dependency-track-tags-support
gschafra Nov 14, 2025
7534f4a
Merge branch 'master' into dependency-track-tags-support
gschafra Dec 11, 2025
f531872
build(deps): Dependency lock fix
gschafra Dec 11, 2025
13e5886
test(cli): Switched to esmock
gschafra Dec 11, 2025
443b7d7
test(cli): Implemented cli sbom submission
gschafra Dec 11, 2025
35d9bf7
chore: Removed obsolete overrides
gschafra Dec 11, 2025
7e1cb13
chore: Removed volta config
gschafra Dec 11, 2025
0bb74fd
chore: Review fixes
gschafra Dec 11, 2025
1392d80
chore: Review fixes
gschafra Dec 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Options:
--project-group Dependency track project group
--project-name Dependency track project name. Default use the directory name
--project-version Dependency track project version [string] [default: ""]
--project-tag Dependency track project tag. Multiple values allowed. [array]
--project-id Dependency track project id. Either provide the id or the project name and version tog
ether [string]
--parent-project-id Dependency track parent project id [string]
Expand Down
3 changes: 3 additions & 0 deletions bin/cdxgen.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ const args = _yargs
default: "",
type: "string",
})
.option("project-tag", {
description: "Dependency track project tag. Multiple values allowed.",
})
.option("project-id", {
description:
"Dependency track project id. Either provide the id or the project name and version together",
Expand Down
1 change: 1 addition & 0 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Options:
--project-group Dependency track project group
--project-name Dependency track project name. Default use the directory name
--project-version Dependency track project version [string] [default: ""]
--project-tag Dependency track project tags. Multiple values allowed. [array]
--project-id Dependency track project id. Either provide the id or the project name and version tog
ether [string]
--parent-project-id Dependency track parent project id [string]
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ Invoke cdxgen with the below arguments to automatically submit the BOM to your o
--project-name Dependency track project name. Default use the di
rectory name
--project-version Dependency track project version [default: ""]
--project-tag Dependency track project tag. Multiple values all
owed. [array]
--project-id Dependency track project id. Either provide the i
d or the project name and version together
--parent-project-id Dependency track parent project id
Expand Down
14 changes: 14 additions & 0 deletions lib/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8717,6 +8717,20 @@ export async function submitBom(args, bomContents) {
) {
bomPayload.parentUUID = args.parentProjectId || args.parentUUID;
}
// Add project tags if provided
// see https://docs.dependencytrack.org/2024/10/01/v4.12.0/
// corresponding API usage documentation can be found on the
// API docs site of your instance, see
// https://docs.dependencytrack.org/integrations/rest-api/
// or public instance see https://yoursky.blue/documentation/rest-api
if (typeof args.projectTag !== "undefined") {
// If args.projectTag is not an array, convert it to an array
// Attention, array items should be of form { name: "tagName " }
// see https://yoursky.blue/documentation/rest-api#tag/bom/operation/UploadBomBase64Encoded
bomPayload.projectTags = (
Array.isArray(args.projectTag) ? args.projectTag : [args.projectTag]
).map((tag) => ({ name: tag }));
}
if (DEBUG_MODE) {
console.log(
"Submitting BOM to",
Expand Down
124 changes: 124 additions & 0 deletions lib/cli/index.poku.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import esmock from "esmock";
import { assert, describe, it } from "poku";
import sinon from "sinon";

describe("CLI tests", () => {
describe("submitBom()", () => {
it("should successfully report the SBOM with given project id, name, version and a single tag", async () => {
const fakeGotResponse = {
json: sinon.stub().resolves({ success: true }),
};

const gotStub = sinon.stub().returns(fakeGotResponse);
gotStub.extend = sinon.stub().returns(gotStub);

const { submitBom } = await esmock("./index.js", {
got: { default: gotStub },
});

const serverUrl = "https://dtrack.example.com";
const projectId = "f7cb9f02-8041-4991-9101-b01fa07a6522";
const projectName = "cdxgen-test-project";
const projectVersion = "1.0.0";
const projectTag = "tag1";
const bomContent = { bom: "test" };
const apiKey = "TEST_API_KEY";
const skipDtTlsCheck = false;

const expectedRequestPayload = {
autoCreate: "true",
bom: "eyJib20iOiJ0ZXN0In0=", // stringified and base64 encoded bomContent
project: projectId,
projectName,
projectVersion,
projectTags: [{ name: projectTag }],
};

await submitBom(
{
serverUrl,
projectId,
projectName,
projectVersion,
apiKey,
skipDtTlsCheck,
projectTag,
},
bomContent,
);

// Verify got was called exactly once
sinon.assert.calledOnce(gotStub);

// Grab call arguments
const [calledUrl, options] = gotStub.firstCall.args;

assert.equal(calledUrl, `${serverUrl}/api/v1/bom`);
assert.equal(options.method, "PUT");
assert.equal(options.https.rejectUnauthorized, !skipDtTlsCheck);
assert.equal(options.headers["X-Api-Key"], apiKey);
assert.match(options.headers["user-agent"], /@CycloneDX\/cdxgen/);
assert.deepEqual(options.json, expectedRequestPayload);
});

it("should successfully report the SBOM with given parent project, name, version and multiple tags", async () => {
const fakeGotResponse = {
json: sinon.stub().resolves({ success: true }),
};

const gotStub = sinon.stub().returns(fakeGotResponse);
gotStub.extend = sinon.stub().returns(gotStub);

const { submitBom } = await esmock("./index.js", {
got: { default: gotStub },
});

const serverUrl = "https://dtrack.example.com";
const projectName = "cdxgen-test-project";
const projectVersion = "1.1.0";
const projectTags = ["tag1", "tag2"];
const parentProjectId = "5103b8b4-4ca3-46ea-8051-036a3b2ab17e";
const bomContent = {
bom: "test2",
};
const apiKey = "TEST_API_KEY";
const skipDtTlsCheck = false;

const expectedRequestPayload = {
autoCreate: "true",
bom: "eyJib20iOiJ0ZXN0MiJ9", // stringified and base64 encoded bomContent
parentUUID: parentProjectId,
projectName,
projectVersion,
projectTags: [{ name: projectTags[0] }, { name: projectTags[1] }],
};

await submitBom(
{
serverUrl,
parentProjectId,
projectName,
projectVersion,
apiKey,
skipDtTlsCheck,
projectTag: projectTags,
},
bomContent,
);

// Verify got was called exactly once
sinon.assert.calledOnce(gotStub);

// Grab call arguments
const [calledUrl, options] = gotStub.firstCall.args;

// Assert call arguments against expectations
assert.equal(calledUrl, `${serverUrl}/api/v1/bom`);
assert.equal(options.method, "PUT");
assert.equal(options.https.rejectUnauthorized, !skipDtTlsCheck);
assert.equal(options.headers["X-Api-Key"], apiKey);
assert.match(options.headers["user-agent"], /@CycloneDX\/cdxgen/);
assert.deepEqual(options.json, expectedRequestPayload);
});
});
});
1 change: 1 addition & 0 deletions lib/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const ALLOWED_PARAMS = [
"projectId",
"projectName",
"projectGroup",
"projectTag",
"projectVersion",
"parentUUID",
"serverUrl",
Expand Down
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@
"@npmcli/package-json": "7.0.4",
"@npmcli/promise-spawn": "9.0.1",
"@npmcli/query": "5.0.0",
"@sinonjs/commons": "3.0.1",
"@sinonjs/fake-timers": "13.0.5",
"@sinonjs/samsam": "8.0.3",
"@sec-ant/readable-stream": "0.6.0",
"@sindresorhus/is": "7.1.1",
"@types/http-cache-semantics": "4.0.4",
Expand Down Expand Up @@ -233,6 +236,7 @@
"escalade": "3.2.0",
"escape-html": "1.0.3",
"escape-string-regexp": "5.0.0",
"esmock": "2.7.3",
"events-universal": "1.0.1",
"expand-template": "2.0.3",
"exponential-backoff": "3.1.3",
Expand Down Expand Up @@ -367,6 +371,7 @@
"side-channel-map": "1.0.1",
"side-channel-weakmap": "1.0.2",
"signal-exit": "4.1.0",
"sinon": "21.0.0",
"simple-concat": "1.0.1",
"simple-get": "4.0.1",
"slice-ansi": "7.1.2",
Expand Down Expand Up @@ -466,7 +471,9 @@
},
"devDependencies": {
"@biomejs/biome": "2.3.8",
"esmock": "^2.7.3",
"poku": "3.0.2",
"sinon": "21.0.0",
"typescript": "5.9.3"
},
"optionalDependencies": {
Expand Down Expand Up @@ -554,6 +561,9 @@
"@npmcli/package-json": "7.0.4",
"@npmcli/promise-spawn": "9.0.1",
"@npmcli/query": "5.0.0",
"@sinonjs/commons": "3.0.1",
"@sinonjs/fake-timers": "13.0.5",
"@sinonjs/samsam": "8.0.3",
"@sec-ant/readable-stream": "0.6.0",
"@sindresorhus/is": "7.1.1",
"@types/http-cache-semantics": "4.0.4",
Expand Down Expand Up @@ -624,6 +634,7 @@
"escalade": "3.2.0",
"escape-html": "1.0.3",
"escape-string-regexp": "5.0.0",
"esmock": "2.7.3",
"events-universal": "1.0.1",
"expand-template": "2.0.3",
"exponential-backoff": "3.1.3",
Expand Down Expand Up @@ -758,6 +769,7 @@
"side-channel-map": "1.0.1",
"side-channel-weakmap": "1.0.2",
"signal-exit": "4.1.0",
"sinon": "21.0.0",
"simple-concat": "1.0.1",
"simple-get": "4.0.1",
"slice-ansi": "7.1.2",
Expand Down
Loading