Skip to content

Commit c32b2b6

Browse files
authoredMar 27, 2025··
Track executables with setuid and setgid flags (#1707)
* Capture setuid and setgid for executables Signed-off-by: Prabhu Subramanian <[email protected]> * Capture setuid and setgid for executables Signed-off-by: Prabhu Subramanian <[email protected]> * Capture setuid and setgid for executables Signed-off-by: Prabhu Subramanian <[email protected]> * Capture sticky bit Signed-off-by: Prabhu Subramanian <[email protected]> --------- Signed-off-by: Prabhu Subramanian <[email protected]>
1 parent 51f0c3d commit c32b2b6

File tree

9 files changed

+150
-96
lines changed

9 files changed

+150
-96
lines changed
 

‎README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ Options:
147147
--server-host Listen address [default: "127.0.0.1"]
148148
--server-port Listen port [default: "9090"]
149149
--install-deps Install dependencies automatically for some projects. Defaults to true but disabled for c
150-
ontainers and oci scans. Use --no-install-deps to disable this feature. [boolean]
150+
ontainers and oci scans. Use --no-install-deps to disable this feature.
151+
[boolean] [default: true]
151152
--validate Validate the generated SBOM using json schema. Defaults to true. Pass --no-validate to di
152153
sable. [boolean] [default: true]
153154
--evidence Generate SBOM with evidence for supported languages. [boolean] [default: false]
@@ -170,6 +171,7 @@ Options:
170171
luated against or attested to.
171172
[array] [choices: "asvs-5.0", "asvs-4.0.3", "bsimm-v13", "masvs-2.0.0", "nist_ssdf-1.1", "pcissc-secure-slc-1.1", "scv
172173
s-1.0.0", "ssaf-DRAFT-2023-11"]
174+
--json-pretty Pretty-print the generated BOM json. [boolean] [default: false]
173175
--min-confidence Minimum confidence needed for the identity of a component from 0 - 1, where 1 is 100% con
174176
fidence. [number] [default: 0]
175177
--technique Analysis technique to use

‎bin/cdxgen.js

+20-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
import { thoughtEnd, thoughtLog } from "../lib/helpers/logger.js";
2525
import {
2626
ATOM_DB,
27+
DEBUG_MODE,
2728
dirNameStr,
2829
getTmpDir,
2930
isMac,
@@ -303,6 +304,11 @@ const args = yargs(hideBin(process.argv))
303304
description:
304305
"Do not show the donation banner. Set this attribute if you are an active sponsor for OWASP CycloneDX.",
305306
})
307+
.option("json-pretty", {
308+
type: "boolean",
309+
default: DEBUG_MODE,
310+
description: "Pretty-print the generated BOM json.",
311+
})
306312
.option("feature-flags", {
307313
description: "Experimental feature flags to enable. Advanced users only.",
308314
hidden: true,
@@ -798,7 +804,11 @@ const checkPermissions = (filePath, options) => {
798804
fs.writeFileSync(jsonFile, bomNSData.bomJson);
799805
jsonPayload = bomNSData.bomJson;
800806
} else {
801-
jsonPayload = JSON.stringify(bomNSData.bomJson, null, null);
807+
jsonPayload = JSON.stringify(
808+
bomNSData.bomJson,
809+
null,
810+
options.jsonPretty ? 2 : null,
811+
);
802812
fs.writeFileSync(jsonFile, jsonPayload);
803813
if (jsonFile.endsWith("bom.json")) {
804814
thoughtLog(
@@ -900,7 +910,11 @@ const checkPermissions = (filePath, options) => {
900910
bomJsonUnsignedObj.signature = signatureBlock;
901911
fs.writeFileSync(
902912
jsonFile,
903-
JSON.stringify(bomJsonUnsignedObj, null, null),
913+
JSON.stringify(
914+
bomJsonUnsignedObj,
915+
null,
916+
options.jsonPretty ? 2 : null,
917+
),
904918
);
905919
thoughtLog(`Signing the BOM file "${jsonFile}".`);
906920
if (publicKeyFile) {
@@ -937,7 +951,9 @@ const checkPermissions = (filePath, options) => {
937951
}
938952
} else if (!options.print) {
939953
if (bomNSData.bomJson) {
940-
console.log(JSON.stringify(bomNSData.bomJson, null, 2));
954+
console.log(
955+
JSON.stringify(bomNSData.bomJson, null, options.jsonPretty ? 2 : null),
956+
);
941957
} else {
942958
console.log("Unable to produce BOM for", filePath);
943959
console.log("Try running the command with -t <type> or -r argument");
@@ -967,6 +983,7 @@ const checkPermissions = (filePath, options) => {
967983
includeCrypto: options.includeCrypto,
968984
specVersion: options.specVersion,
969985
profile: options.profile,
986+
jsonPretty: options.jsonPretty,
970987
};
971988
const dbObjMap = await evinserModule.prepareDB(evinseOptions);
972989
if (dbObjMap) {

‎deno.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@
5050
"imports": {
5151
"@appthreat/atom": "npm:@appthreat/atom@2.1.14",
5252
"@appthreat/cdx-proto": "npm:@appthreat/cdx-proto@1.0.1",
53-
"@babel/parser": "npm:@babel/parser@^7.26.10",
54-
"@babel/traverse": "npm:@babel/traverse@^7.26.10",
53+
"@babel/parser": "npm:@babel/parser@^7.27.0",
54+
"@babel/traverse": "npm:@babel/traverse@^7.27.0",
5555
"@npmcli/arborist": "npm:@npmcli/arborist@9.0.1",
5656
"ajv": "npm:ajv@^8.16.0",
5757
"ajv-formats": "npm:ajv-formats@^3.0.1",

‎docs/CLI.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ Options:
9696
--server-host Listen address [default: "127.0.0.1"]
9797
--server-port Listen port [default: "9090"]
9898
--install-deps Install dependencies automatically for some projects. Defaults to true but disabled for c
99-
ontainers and oci scans. Use --no-install-deps to disable this feature. [boolean]
99+
ontainers and oci scans. Use --no-install-deps to disable this feature.
100+
[boolean] [default: true]
100101
--validate Validate the generated SBOM using json schema. Defaults to true. Pass --no-validate to di
101102
sable. [boolean] [default: true]
102103
--evidence Generate SBOM with evidence for supported languages. [boolean] [default: false]
@@ -119,6 +120,7 @@ Options:
119120
luated against or attested to.
120121
[array] [choices: "asvs-5.0", "asvs-4.0.3", "bsimm-v13", "masvs-2.0.0", "nist_ssdf-1.1", "pcissc-secure-slc-1.1", "scv
121122
s-1.0.0", "ssaf-DRAFT-2023-11"]
123+
--json-pretty Pretty-print the generated BOM json. [boolean] [default: false]
122124
--min-confidence Minimum confidence needed for the identity of a component from 0 - 1, where 1 is 100% con
123125
fidence. [number] [default: 0]
124126
--technique Analysis technique to use

‎lib/evinser/evinser.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,10 @@ export async function createSlice(
257257
const slicesData = createSemanticsSlices(resolve(filePath), options);
258258
// Write the semantics slices data
259259
if (slicesData) {
260-
fs.writeFileSync(slicesFile, JSON.stringify(slicesData, null, null));
260+
fs.writeFileSync(
261+
slicesFile,
262+
JSON.stringify(slicesData, null, options.jsonPretty ? 2 : null),
263+
);
261264
}
262265
return { tempDir: sliceOutputDir, slicesFile };
263266
}
@@ -1404,7 +1407,7 @@ export function createEvinseFile(sliceArtefacts, options) {
14041407
const bomNSData = postProcess({ bomJson }, options);
14051408
fs.writeFileSync(
14061409
evinseOutFile,
1407-
JSON.stringify(bomNSData.bomJson, null, null),
1410+
JSON.stringify(bomNSData.bomJson, null, options.jsonPretty ? 2 : null),
14081411
);
14091412
if (occEvidencePresent || csEvidencePresent || servicesPresent) {
14101413
console.log(evinseOutFile, "created successfully.");

‎lib/managers/binary.js

+34-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
mkdtempSync,
77
readFileSync,
88
rmSync,
9+
statSync,
910
} from "node:fs";
1011
import { arch as _arch, platform as _platform, homedir } from "node:os";
1112
import { basename, dirname, join, resolve } from "node:path";
@@ -1037,16 +1038,45 @@ async function fileComponents(basePath, fileList, fileType) {
10371038
}
10381039
const name = basename(f);
10391040
const purl = `pkg:generic/${name}`;
1041+
let isExecutable;
1042+
let isSetuid;
1043+
let isSetgid;
1044+
let isSticky;
1045+
try {
1046+
const stats = statSync(f);
1047+
const mode = stats.mode;
1048+
isExecutable = !!(mode & 0o111);
1049+
isSetuid = !!(mode & 0o4000);
1050+
isSetgid = !!(mode & 0o2000);
1051+
isSticky = !!(mode & 0o1000);
1052+
} catch (e) {
1053+
// ignore
1054+
}
1055+
const properties = [{ name: "SrcFile", value: f }];
1056+
if (fileType === "executable" && isExecutable !== undefined) {
1057+
properties.push({
1058+
name: `internal:is_${fileType}`,
1059+
value: isExecutable.toString(),
1060+
});
1061+
} else {
1062+
properties.push({ name: `internal:is_${fileType}`, value: "true" });
1063+
}
1064+
if (isSetuid) {
1065+
properties.push({ name: "internal:has_setuid", value: "true" });
1066+
}
1067+
if (isSetgid) {
1068+
properties.push({ name: "internal:has_setgid", value: "true" });
1069+
}
1070+
if (isSticky) {
1071+
properties.push({ name: "internal:has_sticky", value: "true" });
1072+
}
10401073
components.push({
10411074
name,
10421075
type: "file",
10431076
purl,
10441077
"bom-ref": purl,
10451078
hashes,
1046-
properties: [
1047-
{ name: "SrcFile", value: f },
1048-
{ name: `internal:is_${fileType}`, value: "true" },
1049-
],
1079+
properties,
10501080
evidence: {
10511081
identity: [
10521082
{

‎package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@
7474
"*": "biome check --fix --no-errors-on-unmatched"
7575
},
7676
"dependencies": {
77-
"@babel/parser": "^7.26.10",
78-
"@babel/traverse": "^7.26.10",
77+
"@babel/parser": "^7.27.0",
78+
"@babel/traverse": "^7.27.0",
7979
"@iarna/toml": "2.2.5",
8080
"@npmcli/arborist": "9.0.1",
8181
"ajv": "^8.17.1",

‎pnpm-lock.yaml

+80-80
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎types/lib/managers/binary.d.ts.map

+1-1
Original file line numberDiff line numberDiff line change

0 commit comments

Comments
 (0)
Please sign in to comment.