@cyclonedx/cdxgen 12.2.0 → 12.3.0
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/README.md +242 -90
- package/bin/audit.js +191 -0
- package/bin/cdxgen.js +532 -168
- package/bin/convert.js +99 -0
- package/bin/evinse.js +23 -0
- package/bin/repl.js +339 -8
- package/bin/sign.js +8 -0
- package/bin/validate.js +8 -0
- package/bin/verify.js +8 -0
- package/data/container-knowledge-index.json +125 -0
- package/data/gtfobins-index.json +6296 -0
- package/data/lolbas-index.json +150 -0
- package/data/queries-darwin.json +63 -3
- package/data/queries-win.json +45 -3
- package/data/queries.json +74 -2
- package/data/rules/chrome-extensions.yaml +240 -0
- package/data/rules/ci-permissions.yaml +478 -18
- package/data/rules/container-risk.yaml +270 -0
- package/data/rules/obom-runtime.yaml +891 -0
- package/data/rules/package-integrity.yaml +49 -0
- package/data/spdx-export.schema.json +6794 -0
- package/data/spdx-model-v3.0.1.jsonld +15999 -0
- package/lib/audit/index.js +1924 -0
- package/lib/audit/index.poku.js +1488 -0
- package/lib/audit/progress.js +137 -0
- package/lib/audit/progress.poku.js +188 -0
- package/lib/audit/reporters.js +618 -0
- package/lib/audit/scoring.js +310 -0
- package/lib/audit/scoring.poku.js +341 -0
- package/lib/audit/targets.js +260 -0
- package/lib/audit/targets.poku.js +331 -0
- package/lib/cli/index.js +276 -68
- package/lib/cli/index.poku.js +368 -0
- package/lib/helpers/analyzer.js +1052 -5
- package/lib/helpers/analyzer.poku.js +301 -0
- package/lib/helpers/annotationFormatter.js +49 -0
- package/lib/helpers/annotationFormatter.poku.js +44 -0
- package/lib/helpers/bomUtils.js +36 -0
- package/lib/helpers/bomUtils.poku.js +51 -0
- package/lib/helpers/caxa.js +2 -2
- package/lib/helpers/chromextutils.js +1153 -0
- package/lib/helpers/chromextutils.poku.js +493 -0
- package/lib/helpers/ciParsers/githubActions.js +1632 -45
- package/lib/helpers/ciParsers/githubActions.poku.js +853 -1
- package/lib/helpers/containerRisk.js +186 -0
- package/lib/helpers/containerRisk.poku.js +52 -0
- package/lib/helpers/depsUtils.js +16 -0
- package/lib/helpers/depsUtils.poku.js +58 -1
- package/lib/helpers/display.js +245 -61
- package/lib/helpers/display.poku.js +162 -2
- package/lib/helpers/exportUtils.js +123 -0
- package/lib/helpers/exportUtils.poku.js +60 -0
- package/lib/helpers/formulationParsers.js +69 -0
- package/lib/helpers/formulationParsers.poku.js +44 -0
- package/lib/helpers/gtfobins.js +189 -0
- package/lib/helpers/gtfobins.poku.js +49 -0
- package/lib/helpers/lolbas.js +267 -0
- package/lib/helpers/lolbas.poku.js +39 -0
- package/lib/helpers/osqueryTransform.js +84 -0
- package/lib/helpers/osqueryTransform.poku.js +49 -0
- package/lib/helpers/provenanceUtils.js +193 -0
- package/lib/helpers/provenanceUtils.poku.js +145 -0
- package/lib/helpers/pylockutils.js +281 -0
- package/lib/helpers/pylockutils.poku.js +48 -0
- package/lib/helpers/registryProvenance.js +793 -0
- package/lib/helpers/registryProvenance.poku.js +452 -0
- package/lib/helpers/remote/dependency-track.js +84 -0
- package/lib/helpers/remote/dependency-track.poku.js +119 -0
- package/lib/helpers/source.js +1267 -0
- package/lib/helpers/source.poku.js +771 -0
- package/lib/helpers/spdxUtils.js +97 -0
- package/lib/helpers/spdxUtils.poku.js +70 -0
- package/lib/helpers/table.js +384 -0
- package/lib/helpers/table.poku.js +186 -0
- package/lib/helpers/unicodeScan.js +147 -0
- package/lib/helpers/unicodeScan.poku.js +45 -0
- package/lib/helpers/utils.js +882 -136
- package/lib/helpers/utils.poku.js +995 -91
- package/lib/managers/binary.js +29 -5
- package/lib/managers/docker.js +179 -52
- package/lib/managers/docker.poku.js +327 -28
- package/lib/managers/oci.js +107 -23
- package/lib/managers/oci.poku.js +132 -0
- package/lib/server/openapi.yaml +50 -0
- package/lib/server/server.js +228 -331
- package/lib/server/server.poku.js +220 -5
- package/lib/stages/postgen/annotator.js +7 -0
- package/lib/stages/postgen/annotator.poku.js +40 -0
- package/lib/stages/postgen/auditBom.js +20 -5
- package/lib/stages/postgen/auditBom.poku.js +1729 -67
- package/lib/stages/postgen/postgen.js +40 -0
- package/lib/stages/postgen/postgen.poku.js +47 -0
- package/lib/stages/postgen/ruleEngine.js +80 -2
- package/lib/stages/postgen/spdxConverter.js +796 -0
- package/lib/stages/postgen/spdxConverter.poku.js +341 -0
- package/lib/validator/bomValidator.js +232 -0
- package/lib/validator/bomValidator.poku.js +70 -0
- package/lib/validator/complianceRules.js +70 -7
- package/lib/validator/complianceRules.poku.js +30 -0
- package/lib/validator/reporters/annotations.js +2 -2
- package/lib/validator/reporters/console.js +13 -2
- package/lib/validator/reporters.poku.js +13 -0
- package/package.json +10 -8
- package/types/bin/audit.d.ts +3 -0
- package/types/bin/audit.d.ts.map +1 -0
- package/types/bin/convert.d.ts +3 -0
- package/types/bin/convert.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +115 -0
- package/types/lib/audit/index.d.ts.map +1 -0
- package/types/lib/audit/progress.d.ts +27 -0
- package/types/lib/audit/progress.d.ts.map +1 -0
- package/types/lib/audit/reporters.d.ts +35 -0
- package/types/lib/audit/reporters.d.ts.map +1 -0
- package/types/lib/audit/scoring.d.ts +35 -0
- package/types/lib/audit/scoring.d.ts.map +1 -0
- package/types/lib/audit/targets.d.ts +63 -0
- package/types/lib/audit/targets.d.ts.map +1 -0
- package/types/lib/cli/index.d.ts +8 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts +13 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/annotationFormatter.d.ts +23 -0
- package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
- package/types/lib/helpers/bomUtils.d.ts +5 -0
- package/types/lib/helpers/bomUtils.d.ts.map +1 -0
- package/types/lib/helpers/chromextutils.d.ts +97 -0
- package/types/lib/helpers/chromextutils.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/githubActions.d.ts +3 -8
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
- package/types/lib/helpers/containerRisk.d.ts +17 -0
- package/types/lib/helpers/containerRisk.d.ts.map +1 -0
- package/types/lib/helpers/depsUtils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts +4 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/exportUtils.d.ts +40 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -0
- package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
- package/types/lib/helpers/gtfobins.d.ts +17 -0
- package/types/lib/helpers/gtfobins.d.ts.map +1 -0
- package/types/lib/helpers/lolbas.d.ts +16 -0
- package/types/lib/helpers/lolbas.d.ts.map +1 -0
- package/types/lib/helpers/osqueryTransform.d.ts +7 -0
- package/types/lib/helpers/osqueryTransform.d.ts.map +1 -0
- package/types/lib/helpers/provenanceUtils.d.ts +90 -0
- package/types/lib/helpers/provenanceUtils.d.ts.map +1 -0
- package/types/lib/helpers/pylockutils.d.ts +51 -0
- package/types/lib/helpers/pylockutils.d.ts.map +1 -0
- package/types/lib/helpers/registryProvenance.d.ts +17 -0
- package/types/lib/helpers/registryProvenance.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +16 -0
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
- package/types/lib/helpers/source.d.ts +141 -0
- package/types/lib/helpers/source.d.ts.map +1 -0
- package/types/lib/helpers/spdxUtils.d.ts +2 -0
- package/types/lib/helpers/spdxUtils.d.ts.map +1 -0
- package/types/lib/helpers/table.d.ts +6 -0
- package/types/lib/helpers/table.d.ts.map +1 -0
- package/types/lib/helpers/unicodeScan.d.ts +46 -0
- package/types/lib/helpers/unicodeScan.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +30 -11
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +0 -35
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
- package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
- package/types/lib/validator/bomValidator.d.ts +1 -0
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/bin/dependencies.d.ts +0 -3
- package/types/bin/dependencies.d.ts.map +0 -1
- package/types/bin/licenses.d.ts +0 -3
- package/types/bin/licenses.d.ts.map +0 -1
|
@@ -1,15 +1,18 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import esmock from "esmock";
|
|
1
5
|
import { afterEach, assert, beforeEach, describe, it } from "poku";
|
|
6
|
+
import sinon from "sinon";
|
|
2
7
|
|
|
3
|
-
import { isWin } from "../helpers/utils.js";
|
|
4
8
|
import {
|
|
5
|
-
getQueryParams,
|
|
6
9
|
isAllowedHost,
|
|
7
10
|
isAllowedPath,
|
|
8
11
|
isAllowedWinPath,
|
|
9
|
-
parseQueryString,
|
|
10
|
-
parseValue,
|
|
11
12
|
validateAndRejectGitSource,
|
|
12
|
-
} from "
|
|
13
|
+
} from "../helpers/source.js";
|
|
14
|
+
import { isWin } from "../helpers/utils.js";
|
|
15
|
+
import { getQueryParams, parseQueryString, parseValue } from "./server.js";
|
|
13
16
|
|
|
14
17
|
function nullProtoObj(obj) {
|
|
15
18
|
if (obj === null || typeof obj !== "object") {
|
|
@@ -105,6 +108,34 @@ describe("parseQueryString tests", () => {
|
|
|
105
108
|
const result = parseQueryString({}, {}, options);
|
|
106
109
|
checkEqual(result.installDeps, false);
|
|
107
110
|
});
|
|
111
|
+
|
|
112
|
+
it("parses parentProjectName and parentProjectVersion", () => {
|
|
113
|
+
const q = {
|
|
114
|
+
parentProjectName: "parent-app",
|
|
115
|
+
parentProjectVersion: "1.2.3",
|
|
116
|
+
};
|
|
117
|
+
const result = parseQueryString(q, {}, {});
|
|
118
|
+
checkEqual(result.parentProjectName, "parent-app");
|
|
119
|
+
checkEqual(result.parentProjectVersion, "1.2.3");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("parses autoCreate and isLatest boolean options", () => {
|
|
123
|
+
const q = {
|
|
124
|
+
autoCreate: "false",
|
|
125
|
+
isLatest: "true",
|
|
126
|
+
};
|
|
127
|
+
const result = parseQueryString(q, {}, {});
|
|
128
|
+
checkEqual(result.autoCreate, false);
|
|
129
|
+
checkEqual(result.isLatest, true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("parses format for SPDX export requests", () => {
|
|
133
|
+
const q = {
|
|
134
|
+
format: "spdx",
|
|
135
|
+
};
|
|
136
|
+
const result = parseQueryString(q, {}, {});
|
|
137
|
+
checkEqual(result.format, "spdx");
|
|
138
|
+
});
|
|
108
139
|
});
|
|
109
140
|
|
|
110
141
|
describe("isAllowedHost()", () => {
|
|
@@ -586,3 +617,187 @@ it("should correctly normalize and validate various git@ (SCP-like) formats", ()
|
|
|
586
617
|
checkEqual(deniedRes.error, "Host Not Allowed");
|
|
587
618
|
delete process.env.CDXGEN_SERVER_ALLOWED_HOSTS;
|
|
588
619
|
});
|
|
620
|
+
|
|
621
|
+
describe("gitClone() hardening tests", () => {
|
|
622
|
+
it("passes core.hooksPath=/dev/null and --template= flags to git", async () => {
|
|
623
|
+
const spawnStub = sinon.stub().returns({ status: 0, stderr: "" });
|
|
624
|
+
const mkdtempStub = sinon
|
|
625
|
+
.stub()
|
|
626
|
+
.returns(path.join(os.tmpdir(), "fake-repo"));
|
|
627
|
+
|
|
628
|
+
const { gitClone } = await esmock("../helpers/source.js", {
|
|
629
|
+
"../helpers/utils.js": {
|
|
630
|
+
safeSpawnSync: spawnStub,
|
|
631
|
+
isSecureMode: false,
|
|
632
|
+
hasDangerousUnicode: sinon.stub().returns(false),
|
|
633
|
+
getTmpDir: sinon.stub().returns(os.tmpdir()),
|
|
634
|
+
},
|
|
635
|
+
"node:fs": {
|
|
636
|
+
mkdtempSync: mkdtempStub,
|
|
637
|
+
existsSync: sinon.stub().returns(false),
|
|
638
|
+
readdirSync: sinon.stub().returns([]),
|
|
639
|
+
statSync: sinon.stub().returns({ isDirectory: () => true }),
|
|
640
|
+
readFileSync: sinon.stub().returns(""),
|
|
641
|
+
},
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
gitClone("https://example.com/repo.git");
|
|
645
|
+
|
|
646
|
+
sinon.assert.calledOnce(spawnStub);
|
|
647
|
+
const [cmd, args] = spawnStub.firstCall.args;
|
|
648
|
+
assert.strictEqual(cmd, "git");
|
|
649
|
+
|
|
650
|
+
// core.hooksPath=/dev/null must be present as a -c flag
|
|
651
|
+
const hooksPathIdx = args.indexOf("core.hooksPath=/dev/null");
|
|
652
|
+
assert.ok(
|
|
653
|
+
hooksPathIdx > 0 && args[hooksPathIdx - 1] === "-c",
|
|
654
|
+
"expected -c core.hooksPath=/dev/null in git args",
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
// --template= must appear after "clone"
|
|
658
|
+
const cloneIdx = args.indexOf("clone");
|
|
659
|
+
assert.ok(cloneIdx >= 0, "expected 'clone' subcommand in git args");
|
|
660
|
+
assert.ok(
|
|
661
|
+
args.slice(cloneIdx).includes("--template="),
|
|
662
|
+
"expected --template= after clone in git args",
|
|
663
|
+
);
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it("uses GIT_CONFIG_GLOBAL=/dev/null instead of invalid GIT_CONFIG_NOGLOBAL in secure mode", async () => {
|
|
667
|
+
const spawnStub = sinon.stub().returns({ status: 0, stderr: "" });
|
|
668
|
+
const mkdtempStub = sinon
|
|
669
|
+
.stub()
|
|
670
|
+
.returns(path.join(os.tmpdir(), "fake-repo"));
|
|
671
|
+
|
|
672
|
+
const { gitClone } = await esmock("../helpers/source.js", {
|
|
673
|
+
"../helpers/utils.js": {
|
|
674
|
+
safeSpawnSync: spawnStub,
|
|
675
|
+
isSecureMode: true,
|
|
676
|
+
hasDangerousUnicode: sinon.stub().returns(false),
|
|
677
|
+
getTmpDir: sinon.stub().returns(os.tmpdir()),
|
|
678
|
+
},
|
|
679
|
+
"node:fs": {
|
|
680
|
+
mkdtempSync: mkdtempStub,
|
|
681
|
+
existsSync: sinon.stub().returns(false),
|
|
682
|
+
readdirSync: sinon.stub().returns([]),
|
|
683
|
+
statSync: sinon.stub().returns({ isDirectory: () => true }),
|
|
684
|
+
readFileSync: sinon.stub().returns(""),
|
|
685
|
+
},
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
gitClone("https://example.com/repo.git");
|
|
689
|
+
|
|
690
|
+
sinon.assert.calledOnce(spawnStub);
|
|
691
|
+
const opts = spawnStub.firstCall.args[2];
|
|
692
|
+
assert.ok(opts.env, "expected env to be set");
|
|
693
|
+
assert.ok(
|
|
694
|
+
!("GIT_CONFIG_NOGLOBAL" in opts.env),
|
|
695
|
+
"GIT_CONFIG_NOGLOBAL must not be set (it is not a valid Git env var)",
|
|
696
|
+
);
|
|
697
|
+
assert.strictEqual(
|
|
698
|
+
opts.env.GIT_CONFIG_GLOBAL,
|
|
699
|
+
"/dev/null",
|
|
700
|
+
"GIT_CONFIG_GLOBAL must be /dev/null in secure mode",
|
|
701
|
+
);
|
|
702
|
+
assert.strictEqual(
|
|
703
|
+
opts.env.GIT_CONFIG_NOSYSTEM,
|
|
704
|
+
"1",
|
|
705
|
+
"GIT_CONFIG_NOSYSTEM must be '1' in secure mode",
|
|
706
|
+
);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
it("sets GIT_TERMINAL_PROMPT=0 in both secure and non-secure mode", async () => {
|
|
710
|
+
for (const secureMode of [false, true]) {
|
|
711
|
+
const spawnStub = sinon.stub().returns({ status: 0, stderr: "" });
|
|
712
|
+
const mkdtempStub = sinon
|
|
713
|
+
.stub()
|
|
714
|
+
.returns(path.join(os.tmpdir(), "fake-repo"));
|
|
715
|
+
|
|
716
|
+
const { gitClone } = await esmock("../helpers/source.js", {
|
|
717
|
+
"../helpers/utils.js": {
|
|
718
|
+
safeSpawnSync: spawnStub,
|
|
719
|
+
isSecureMode: secureMode,
|
|
720
|
+
hasDangerousUnicode: sinon.stub().returns(false),
|
|
721
|
+
getTmpDir: sinon.stub().returns(os.tmpdir()),
|
|
722
|
+
},
|
|
723
|
+
"node:fs": {
|
|
724
|
+
mkdtempSync: mkdtempStub,
|
|
725
|
+
existsSync: sinon.stub().returns(false),
|
|
726
|
+
readdirSync: sinon.stub().returns([]),
|
|
727
|
+
statSync: sinon.stub().returns({ isDirectory: () => true }),
|
|
728
|
+
readFileSync: sinon.stub().returns(""),
|
|
729
|
+
},
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
gitClone("https://example.com/repo.git");
|
|
733
|
+
|
|
734
|
+
const opts = spawnStub.firstCall.args[2];
|
|
735
|
+
assert.strictEqual(
|
|
736
|
+
opts.env.GIT_TERMINAL_PROMPT,
|
|
737
|
+
"0",
|
|
738
|
+
`GIT_TERMINAL_PROMPT must be '0' when isSecureMode=${secureMode}`,
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
it("inserts --branch before repoUrl when a valid branch is specified", async () => {
|
|
744
|
+
const spawnStub = sinon.stub().returns({ status: 0, stderr: "" });
|
|
745
|
+
const mkdtempStub = sinon
|
|
746
|
+
.stub()
|
|
747
|
+
.returns(path.join(os.tmpdir(), "fake-repo"));
|
|
748
|
+
|
|
749
|
+
const { gitClone } = await esmock("../helpers/source.js", {
|
|
750
|
+
"../helpers/utils.js": {
|
|
751
|
+
safeSpawnSync: spawnStub,
|
|
752
|
+
isSecureMode: false,
|
|
753
|
+
hasDangerousUnicode: sinon.stub().returns(false),
|
|
754
|
+
getTmpDir: sinon.stub().returns(os.tmpdir()),
|
|
755
|
+
},
|
|
756
|
+
"node:fs": {
|
|
757
|
+
mkdtempSync: mkdtempStub,
|
|
758
|
+
existsSync: sinon.stub().returns(false),
|
|
759
|
+
readdirSync: sinon.stub().returns([]),
|
|
760
|
+
statSync: sinon.stub().returns({ isDirectory: () => true }),
|
|
761
|
+
readFileSync: sinon.stub().returns(""),
|
|
762
|
+
},
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
gitClone("https://example.com/repo.git", "main");
|
|
766
|
+
|
|
767
|
+
const [, args] = spawnStub.firstCall.args;
|
|
768
|
+
const branchIdx = args.indexOf("--branch");
|
|
769
|
+
assert.ok(branchIdx >= 0, "expected --branch in git args");
|
|
770
|
+
assert.strictEqual(args[branchIdx + 1], "main");
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it("skips --branch when the branch name starts with a dash", async () => {
|
|
774
|
+
const spawnStub = sinon.stub().returns({ status: 0, stderr: "" });
|
|
775
|
+
const mkdtempStub = sinon
|
|
776
|
+
.stub()
|
|
777
|
+
.returns(path.join(os.tmpdir(), "fake-repo"));
|
|
778
|
+
|
|
779
|
+
const { gitClone } = await esmock("../helpers/source.js", {
|
|
780
|
+
"../helpers/utils.js": {
|
|
781
|
+
safeSpawnSync: spawnStub,
|
|
782
|
+
isSecureMode: false,
|
|
783
|
+
hasDangerousUnicode: sinon.stub().returns(false),
|
|
784
|
+
getTmpDir: sinon.stub().returns(os.tmpdir()),
|
|
785
|
+
},
|
|
786
|
+
"node:fs": {
|
|
787
|
+
mkdtempSync: mkdtempStub,
|
|
788
|
+
existsSync: sinon.stub().returns(false),
|
|
789
|
+
readdirSync: sinon.stub().returns([]),
|
|
790
|
+
statSync: sinon.stub().returns({ isDirectory: () => true }),
|
|
791
|
+
readFileSync: sinon.stub().returns(""),
|
|
792
|
+
},
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
gitClone("https://example.com/repo.git", "--malicious");
|
|
796
|
+
|
|
797
|
+
const [, args] = spawnStub.firstCall.args;
|
|
798
|
+
assert.ok(
|
|
799
|
+
!args.includes("--branch"),
|
|
800
|
+
"must not include --branch for dash-prefixed branch names",
|
|
801
|
+
);
|
|
802
|
+
});
|
|
803
|
+
});
|
|
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { thoughtLog } from "../../helpers/logger.js";
|
|
5
|
+
import { getTrustedPublishingComponentCounts } from "../../helpers/provenanceUtils.js";
|
|
5
6
|
import { dirNameStr } from "../../helpers/utils.js";
|
|
6
7
|
|
|
7
8
|
// Tags per BOM type.
|
|
@@ -295,6 +296,9 @@ export function textualMetadata(bomJson) {
|
|
|
295
296
|
c?.purl?.startsWith("pkg:swid"),
|
|
296
297
|
).length;
|
|
297
298
|
const githubStats = getGitHubWorkflowStats(bomJson?.components);
|
|
299
|
+
const trustedPublishingCounts = getTrustedPublishingComponentCounts(
|
|
300
|
+
bomJson?.components,
|
|
301
|
+
);
|
|
298
302
|
const isGitHubBom = bomType === "SBOM";
|
|
299
303
|
if (metadata?.timestamp) {
|
|
300
304
|
text = `This ${bomTypeDescription} document was created on ${humanifyTimestamp(metadata.timestamp)}`;
|
|
@@ -469,6 +473,9 @@ export function textualMetadata(bomJson) {
|
|
|
469
473
|
if (!isGitHubBom) {
|
|
470
474
|
text = `${text} There are ${bomJson.components.length} components.`;
|
|
471
475
|
}
|
|
476
|
+
if (trustedPublishingCounts.total > 0) {
|
|
477
|
+
text = `${text} Trusted publishing metadata is present for ${trustedPublishingCounts.npm} npm component(s) and ${trustedPublishingCounts.pypi} PyPI component(s).`;
|
|
478
|
+
}
|
|
472
479
|
} else {
|
|
473
480
|
text = `${text} BOM file is empty without components.`;
|
|
474
481
|
thoughtLog(
|
|
@@ -263,6 +263,46 @@ it("textualMetadata tests", () => {
|
|
|
263
263
|
}),
|
|
264
264
|
"This Operations Bill-of-Materials (OBOM) document was created on Monday, November 11, 2024 with cdxgen. The lifecycles phases represented are: pre-build and operations. The document describes an operating system named 'Microsoft Windows 11 Pro' with version '22H2'. BOM file is empty without components. The OS uses the x64 architecture and has the build version '10.0.22621'.",
|
|
265
265
|
);
|
|
266
|
+
|
|
267
|
+
assert.ok(
|
|
268
|
+
textualMetadata({
|
|
269
|
+
metadata: {
|
|
270
|
+
component: {
|
|
271
|
+
name: "trusted-demo",
|
|
272
|
+
type: "application",
|
|
273
|
+
version: "1.0.0",
|
|
274
|
+
},
|
|
275
|
+
timestamp: "2026-01-01T00:00:00Z",
|
|
276
|
+
tools: {
|
|
277
|
+
components: [{ name: "cdxgen" }],
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
components: [
|
|
281
|
+
{
|
|
282
|
+
name: "left-pad",
|
|
283
|
+
properties: [
|
|
284
|
+
{
|
|
285
|
+
name: "cdx:npm:trustedPublishing",
|
|
286
|
+
value: "true",
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
type: "library",
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "requests",
|
|
293
|
+
properties: [
|
|
294
|
+
{
|
|
295
|
+
name: "cdx:pypi:trustedPublishing",
|
|
296
|
+
value: "true",
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
type: "library",
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
}).includes(
|
|
303
|
+
"Trusted publishing metadata is present for 1 npm component(s) and 1 PyPI component(s).",
|
|
304
|
+
),
|
|
305
|
+
);
|
|
266
306
|
});
|
|
267
307
|
|
|
268
308
|
it("extractTags tests", () => {
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import { buildAnnotationText } from "../../helpers/annotationFormatter.js";
|
|
9
|
+
import { table } from "../../helpers/table.js";
|
|
10
10
|
import {
|
|
11
11
|
DEBUG_MODE,
|
|
12
12
|
getTimestamp,
|
|
@@ -16,7 +16,6 @@ import { evaluateRules, loadRules } from "./ruleEngine.js";
|
|
|
16
16
|
|
|
17
17
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
18
18
|
const BUILTIN_RULES_DIR = join(__dirname, "..", "..", "..", "data", "rules");
|
|
19
|
-
const CODE_BLOCK = "```";
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* Audit BOM formulation section using JSONata-powered rule engine
|
|
@@ -90,6 +89,7 @@ export function formatConsoleOutput(findings) {
|
|
|
90
89
|
columnDefault: { wrapWord: true, width: 100 },
|
|
91
90
|
columns: [
|
|
92
91
|
{ width: 10 },
|
|
92
|
+
{ width: 26 },
|
|
93
93
|
{ width: 35 },
|
|
94
94
|
{ width: 50 },
|
|
95
95
|
{ width: 50 },
|
|
@@ -100,10 +100,13 @@ export function formatConsoleOutput(findings) {
|
|
|
100
100
|
content: "BOM Audit Findings\nGenerated with \u2665 by cdxgen",
|
|
101
101
|
},
|
|
102
102
|
};
|
|
103
|
-
const data = [["Rule", "Message", "Description", "Ref", "File"]];
|
|
103
|
+
const data = [["Rule", "ATT&CK", "Message", "Description", "Ref", "File"]];
|
|
104
104
|
for (const f of findings) {
|
|
105
105
|
const line = [];
|
|
106
106
|
line.push(f.ruleId);
|
|
107
|
+
line.push(
|
|
108
|
+
[...(f.attackTactics || []), ...(f.attackTechniques || [])].join(", "),
|
|
109
|
+
);
|
|
107
110
|
line.push(f.message);
|
|
108
111
|
line.push(f.description || "");
|
|
109
112
|
line.push(f.location?.purl || f.location?.bomRef || "");
|
|
@@ -144,6 +147,18 @@ export function formatAnnotations(findings, bomJson) {
|
|
|
144
147
|
if (f.mitigation) {
|
|
145
148
|
properties.push({ name: "cdx:audit:mitigation", value: f.mitigation });
|
|
146
149
|
}
|
|
150
|
+
if (f.attackTactics?.length) {
|
|
151
|
+
properties.push({
|
|
152
|
+
name: "cdx:audit:attack:tactics",
|
|
153
|
+
value: f.attackTactics.join(","),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (f.attackTechniques?.length) {
|
|
157
|
+
properties.push({
|
|
158
|
+
name: "cdx:audit:attack:techniques",
|
|
159
|
+
value: f.attackTechniques.join(","),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
147
162
|
if (f?.location?.purl) {
|
|
148
163
|
properties.push({
|
|
149
164
|
name: "cdx:audit:location:purl",
|
|
@@ -178,7 +193,7 @@ export function formatAnnotations(findings, bomJson) {
|
|
|
178
193
|
component: cdxgenAnnotator[0],
|
|
179
194
|
},
|
|
180
195
|
timestamp: getTimestamp(),
|
|
181
|
-
text:
|
|
196
|
+
text: buildAnnotationText(f.message, properties),
|
|
182
197
|
};
|
|
183
198
|
});
|
|
184
199
|
}
|