cognium-dev 3.200.0 → 3.202.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/dist/cli.js +374 -9
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -3,9 +3,9 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// src/cli.ts
|
|
6
|
-
import { readFileSync, existsSync } from "fs";
|
|
6
|
+
import { readFileSync, existsSync, writeFileSync } from "fs";
|
|
7
7
|
import { stat as stat2, readdir as readdir2 } from "fs/promises";
|
|
8
|
-
import { join as join2, dirname as dirname3, extname, resolve as resolve2, relative as relative4 } from "path";
|
|
8
|
+
import { join as join2, dirname as dirname3, extname, resolve as resolve2, relative as relative4, basename } from "path";
|
|
9
9
|
import { createRequire as createRequire2 } from "module";
|
|
10
10
|
|
|
11
11
|
// ../../node_modules/web-tree-sitter/web-tree-sitter.js
|
|
@@ -24488,6 +24488,10 @@ var JS_TAINTED_PATTERNS = [
|
|
|
24488
24488
|
{ pattern: /\blocation\.href\b/, type: "http_path" },
|
|
24489
24489
|
{ pattern: /\bdocument\.getElementById\b/, type: "dom_input" },
|
|
24490
24490
|
{ pattern: /\bdocument\.querySelector\b/, type: "dom_input" },
|
|
24491
|
+
{ pattern: /\bdocument\.getElementsByName\b/, type: "dom_input" },
|
|
24492
|
+
{ pattern: /\bdocument\.getElementsByClassName\b/, type: "dom_input" },
|
|
24493
|
+
{ pattern: /\bdocument\.getElementsByTagName\b/, type: "dom_input" },
|
|
24494
|
+
{ pattern: /\bdocument\.querySelectorAll\b/, type: "dom_input" },
|
|
24491
24495
|
{ pattern: /\b(?:event|e)\.(?:target\.)?value\b/, type: "dom_input" },
|
|
24492
24496
|
{ pattern: /\bdocument\.referrer\b/, type: "http_header" },
|
|
24493
24497
|
{ pattern: /\bdocument\.cookie\b/, type: "http_cookie" },
|
|
@@ -45900,6 +45904,244 @@ function deriveProjectRoot(paths) {
|
|
|
45900
45904
|
}
|
|
45901
45905
|
return common.join("/") || "/";
|
|
45902
45906
|
}
|
|
45907
|
+
// ../circle-ir/dist/analysis/sbom.js
|
|
45908
|
+
function purlVersion(spec) {
|
|
45909
|
+
const stripped = spec.trim().replace(/^[\s^~=<>]+/, "");
|
|
45910
|
+
if (/^v?[0-9][\w.\-+]*$/.test(stripped))
|
|
45911
|
+
return stripped;
|
|
45912
|
+
return;
|
|
45913
|
+
}
|
|
45914
|
+
function encodePurlName(name2) {
|
|
45915
|
+
return name2.split("/").map((seg) => encodeURIComponent(seg)).join("/");
|
|
45916
|
+
}
|
|
45917
|
+
function makePurl(ecosystem, name2, version) {
|
|
45918
|
+
const v = purlVersion(version);
|
|
45919
|
+
const path = ecosystem === "maven" ? name2.replace(":", "/") : name2;
|
|
45920
|
+
const base = `pkg:${ecosystem}/${encodePurlName(path)}`;
|
|
45921
|
+
return v ? `${base}@${encodeURIComponent(v)}` : base;
|
|
45922
|
+
}
|
|
45923
|
+
function dep(ecosystem, name2, version, scope) {
|
|
45924
|
+
const v = version.trim() || "unknown";
|
|
45925
|
+
return { name: name2, version: v, ecosystem, scope, purl: makePurl(ecosystem, name2, v) };
|
|
45926
|
+
}
|
|
45927
|
+
function parseNpmDependencies(packageJson) {
|
|
45928
|
+
if (!packageJson)
|
|
45929
|
+
return [];
|
|
45930
|
+
let parsed;
|
|
45931
|
+
try {
|
|
45932
|
+
parsed = JSON.parse(packageJson);
|
|
45933
|
+
} catch {
|
|
45934
|
+
return [];
|
|
45935
|
+
}
|
|
45936
|
+
if (!parsed || typeof parsed !== "object")
|
|
45937
|
+
return [];
|
|
45938
|
+
const obj = parsed;
|
|
45939
|
+
const out2 = [];
|
|
45940
|
+
const sections = [
|
|
45941
|
+
["dependencies", "required"],
|
|
45942
|
+
["optionalDependencies", "optional"],
|
|
45943
|
+
["peerDependencies", "optional"],
|
|
45944
|
+
["devDependencies", "dev"]
|
|
45945
|
+
];
|
|
45946
|
+
for (const [key, scope] of sections) {
|
|
45947
|
+
const block = obj[key];
|
|
45948
|
+
if (!block || typeof block !== "object")
|
|
45949
|
+
continue;
|
|
45950
|
+
for (const [name2, spec] of Object.entries(block)) {
|
|
45951
|
+
out2.push(dep("npm", name2, typeof spec === "string" ? spec : "unknown", scope));
|
|
45952
|
+
}
|
|
45953
|
+
}
|
|
45954
|
+
return out2;
|
|
45955
|
+
}
|
|
45956
|
+
function parsePypiDependencies(requirementsTxt) {
|
|
45957
|
+
if (!requirementsTxt)
|
|
45958
|
+
return [];
|
|
45959
|
+
const out2 = [];
|
|
45960
|
+
for (const raw of requirementsTxt.split(/\r?\n/)) {
|
|
45961
|
+
let line = raw.trim();
|
|
45962
|
+
if (!line || line.startsWith("#"))
|
|
45963
|
+
continue;
|
|
45964
|
+
if (line.startsWith("-") || /^[a-z]+:\/\//i.test(line))
|
|
45965
|
+
continue;
|
|
45966
|
+
line = line.split("#")[0].trim();
|
|
45967
|
+
line = line.split(";")[0].trim();
|
|
45968
|
+
if (!line)
|
|
45969
|
+
continue;
|
|
45970
|
+
const m = line.match(/^([A-Za-z0-9._-]+)\s*(?:\[[^\]]*\])?\s*(.*)$/);
|
|
45971
|
+
if (!m)
|
|
45972
|
+
continue;
|
|
45973
|
+
const name2 = m[1];
|
|
45974
|
+
const rest = m[2].trim();
|
|
45975
|
+
const verMatch = rest.match(/^(?:==|>=|<=|~=|!=|>|<|===)?\s*([^,\s]+)/);
|
|
45976
|
+
const version = verMatch && verMatch[1] ? verMatch[1] : "unknown";
|
|
45977
|
+
out2.push(dep("pypi", name2, version, "required"));
|
|
45978
|
+
}
|
|
45979
|
+
return out2;
|
|
45980
|
+
}
|
|
45981
|
+
function parseMavenDependencies(pomXml) {
|
|
45982
|
+
if (!pomXml)
|
|
45983
|
+
return [];
|
|
45984
|
+
const out2 = [];
|
|
45985
|
+
const depRe = /<dependency>([\s\S]*?)<\/dependency>/g;
|
|
45986
|
+
let m;
|
|
45987
|
+
while ((m = depRe.exec(pomXml)) !== null) {
|
|
45988
|
+
const block = m[1];
|
|
45989
|
+
const gid = block.match(/<groupId>\s*([^<\s]+)\s*<\/groupId>/)?.[1];
|
|
45990
|
+
const aid = block.match(/<artifactId>\s*([^<\s]+)\s*<\/artifactId>/)?.[1];
|
|
45991
|
+
if (!gid || !aid)
|
|
45992
|
+
continue;
|
|
45993
|
+
let version = block.match(/<version>\s*([^<\s]+)\s*<\/version>/)?.[1] ?? "unknown";
|
|
45994
|
+
if (version.startsWith("${"))
|
|
45995
|
+
version = "unknown";
|
|
45996
|
+
const mvnScope = block.match(/<scope>\s*([^<\s]+)\s*<\/scope>/)?.[1];
|
|
45997
|
+
const optional = /<optional>\s*true\s*<\/optional>/.test(block);
|
|
45998
|
+
const scope = mvnScope === "test" || mvnScope === "provided" ? "dev" : optional ? "optional" : "required";
|
|
45999
|
+
out2.push(dep("maven", `${gid}:${aid}`, version, scope));
|
|
46000
|
+
}
|
|
46001
|
+
return out2;
|
|
46002
|
+
}
|
|
46003
|
+
function parseCargoDependencies(cargoToml) {
|
|
46004
|
+
if (!cargoToml)
|
|
46005
|
+
return [];
|
|
46006
|
+
const out2 = [];
|
|
46007
|
+
let scope = null;
|
|
46008
|
+
for (const raw of cargoToml.split(/\r?\n/)) {
|
|
46009
|
+
const line = raw.trim();
|
|
46010
|
+
if (!line || line.startsWith("#"))
|
|
46011
|
+
continue;
|
|
46012
|
+
const section = line.match(/^\[([^\]]+)\]$/);
|
|
46013
|
+
if (section) {
|
|
46014
|
+
const s = section[1];
|
|
46015
|
+
if (s === "dependencies")
|
|
46016
|
+
scope = "required";
|
|
46017
|
+
else if (s === "dev-dependencies")
|
|
46018
|
+
scope = "dev";
|
|
46019
|
+
else if (s === "build-dependencies")
|
|
46020
|
+
scope = "optional";
|
|
46021
|
+
else
|
|
46022
|
+
scope = null;
|
|
46023
|
+
continue;
|
|
46024
|
+
}
|
|
46025
|
+
if (!scope)
|
|
46026
|
+
continue;
|
|
46027
|
+
const kv = line.match(/^([A-Za-z0-9._-]+)\s*=\s*(.+)$/);
|
|
46028
|
+
if (!kv)
|
|
46029
|
+
continue;
|
|
46030
|
+
const name2 = kv[1];
|
|
46031
|
+
const value = kv[2].trim();
|
|
46032
|
+
let version = "unknown";
|
|
46033
|
+
if (value.startsWith('"') || value.startsWith("'")) {
|
|
46034
|
+
version = value.replace(/^["']|["'].*$/g, "");
|
|
46035
|
+
} else {
|
|
46036
|
+
const inline = value.match(/version\s*=\s*["']([^"']+)["']/);
|
|
46037
|
+
if (inline)
|
|
46038
|
+
version = inline[1];
|
|
46039
|
+
}
|
|
46040
|
+
out2.push(dep("cargo", name2, version, scope));
|
|
46041
|
+
}
|
|
46042
|
+
return out2;
|
|
46043
|
+
}
|
|
46044
|
+
function parseGoDependencies(goMod) {
|
|
46045
|
+
if (!goMod)
|
|
46046
|
+
return [];
|
|
46047
|
+
const out2 = [];
|
|
46048
|
+
let inBlock = false;
|
|
46049
|
+
for (const raw of goMod.split(/\r?\n/)) {
|
|
46050
|
+
const line = raw.trim();
|
|
46051
|
+
if (!line || line.startsWith("//"))
|
|
46052
|
+
continue;
|
|
46053
|
+
if (!inBlock && /^require\s*\($/.test(line)) {
|
|
46054
|
+
inBlock = true;
|
|
46055
|
+
continue;
|
|
46056
|
+
}
|
|
46057
|
+
if (inBlock && line === ")") {
|
|
46058
|
+
inBlock = false;
|
|
46059
|
+
continue;
|
|
46060
|
+
}
|
|
46061
|
+
let spec;
|
|
46062
|
+
if (inBlock)
|
|
46063
|
+
spec = line;
|
|
46064
|
+
else if (/^require\s+/.test(line))
|
|
46065
|
+
spec = line.replace(/^require\s+/, "");
|
|
46066
|
+
else
|
|
46067
|
+
continue;
|
|
46068
|
+
const indirect = /\/\/\s*indirect/.test(spec);
|
|
46069
|
+
spec = spec.replace(/\/\/.*$/, "").trim();
|
|
46070
|
+
const m = spec.match(/^(\S+)\s+(\S+)$/);
|
|
46071
|
+
if (!m)
|
|
46072
|
+
continue;
|
|
46073
|
+
out2.push(dep("golang", m[1], m[2], indirect ? "optional" : "required"));
|
|
46074
|
+
}
|
|
46075
|
+
return out2;
|
|
46076
|
+
}
|
|
46077
|
+
function toCycloneDx(deps, meta = {}) {
|
|
46078
|
+
const bom = {
|
|
46079
|
+
bomFormat: "CycloneDX",
|
|
46080
|
+
specVersion: "1.5",
|
|
46081
|
+
version: 1
|
|
46082
|
+
};
|
|
46083
|
+
if (meta.serialNumber)
|
|
46084
|
+
bom.serialNumber = meta.serialNumber;
|
|
46085
|
+
const metadata2 = {};
|
|
46086
|
+
if (meta.timestamp)
|
|
46087
|
+
metadata2.timestamp = meta.timestamp;
|
|
46088
|
+
metadata2.tools = [{ vendor: "Cognium", name: meta.tool ?? "circle-ir" }];
|
|
46089
|
+
if (meta.name) {
|
|
46090
|
+
metadata2.component = {
|
|
46091
|
+
type: "application",
|
|
46092
|
+
name: meta.name,
|
|
46093
|
+
...meta.version ? { version: meta.version } : {}
|
|
46094
|
+
};
|
|
46095
|
+
}
|
|
46096
|
+
bom.metadata = metadata2;
|
|
46097
|
+
bom.components = deps.map((d) => ({
|
|
46098
|
+
type: "library",
|
|
46099
|
+
name: d.name,
|
|
46100
|
+
version: d.version,
|
|
46101
|
+
purl: d.purl,
|
|
46102
|
+
scope: d.scope === "dev" ? "excluded" : d.scope === "optional" ? "optional" : "required",
|
|
46103
|
+
"bom-ref": d.purl
|
|
46104
|
+
}));
|
|
46105
|
+
return bom;
|
|
46106
|
+
}
|
|
46107
|
+
function spdxId(raw) {
|
|
46108
|
+
return raw.replace(/[^a-zA-Z0-9.-]/g, "-");
|
|
46109
|
+
}
|
|
46110
|
+
function toSpdx(deps, meta = {}) {
|
|
46111
|
+
const docName = meta.name ?? "document";
|
|
46112
|
+
const packages = deps.map((d, i2) => ({
|
|
46113
|
+
SPDXID: `SPDXRef-Package-${spdxId(d.ecosystem)}-${spdxId(d.name)}-${i2}`,
|
|
46114
|
+
name: d.name,
|
|
46115
|
+
versionInfo: d.version,
|
|
46116
|
+
downloadLocation: "NOASSERTION",
|
|
46117
|
+
filesAnalyzed: false,
|
|
46118
|
+
externalRefs: [
|
|
46119
|
+
{
|
|
46120
|
+
referenceCategory: "PACKAGE-MANAGER",
|
|
46121
|
+
referenceType: "purl",
|
|
46122
|
+
referenceLocator: d.purl
|
|
46123
|
+
}
|
|
46124
|
+
]
|
|
46125
|
+
}));
|
|
46126
|
+
const relationships = packages.map((p) => ({
|
|
46127
|
+
spdxElementId: "SPDXRef-DOCUMENT",
|
|
46128
|
+
relatedSpdxElement: p.SPDXID,
|
|
46129
|
+
relationshipType: "DESCRIBES"
|
|
46130
|
+
}));
|
|
46131
|
+
return {
|
|
46132
|
+
spdxVersion: "SPDX-2.3",
|
|
46133
|
+
dataLicense: "CC0-1.0",
|
|
46134
|
+
SPDXID: "SPDXRef-DOCUMENT",
|
|
46135
|
+
name: docName,
|
|
46136
|
+
documentNamespace: meta.namespace ?? `https://cognium.dev/spdxdocs/${spdxId(docName)}`,
|
|
46137
|
+
creationInfo: {
|
|
46138
|
+
created: meta.timestamp ?? "1970-01-01T00:00:00Z",
|
|
46139
|
+
creators: [`Tool: ${meta.tool ?? "circle-ir"}`]
|
|
46140
|
+
},
|
|
46141
|
+
packages,
|
|
46142
|
+
relationships
|
|
46143
|
+
};
|
|
46144
|
+
}
|
|
45903
46145
|
// ../project-profile-detect/dist/index.js
|
|
45904
46146
|
import { relative as relative3 } from "path";
|
|
45905
46147
|
|
|
@@ -46511,7 +46753,7 @@ var colors = {
|
|
|
46511
46753
|
};
|
|
46512
46754
|
|
|
46513
46755
|
// src/version.ts
|
|
46514
|
-
var version = "3.
|
|
46756
|
+
var version = "3.202.0";
|
|
46515
46757
|
|
|
46516
46758
|
// src/formatters.ts
|
|
46517
46759
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
|
@@ -47112,6 +47354,7 @@ USAGE:
|
|
|
47112
47354
|
COMMANDS:
|
|
47113
47355
|
scan <path> Scan files or directories for security vulnerabilities
|
|
47114
47356
|
metrics <path> Report software quality metrics for files or directories
|
|
47357
|
+
sbom <path> Generate a Software Bill of Materials from project manifests
|
|
47115
47358
|
list-passes [cat] List all analysis passes (optionally filter by category)
|
|
47116
47359
|
init Initialize a configuration file in your project
|
|
47117
47360
|
version Display version information
|
|
@@ -47160,6 +47403,17 @@ METRICS OPTIONS:
|
|
|
47160
47403
|
-o, --output <file> Write results to file
|
|
47161
47404
|
-q, --quiet Suppress progress output
|
|
47162
47405
|
|
|
47406
|
+
SBOM OPTIONS:
|
|
47407
|
+
-f, --format <format> SBOM format (cyclonedx|spdx) [default: cyclonedx]
|
|
47408
|
+
- cyclonedx: CycloneDX 1.5 JSON (OWASP)
|
|
47409
|
+
- spdx: SPDX 2.3 JSON (Linux Foundation)
|
|
47410
|
+
--name <name> Project name in the document [default: package.json name or dir]
|
|
47411
|
+
--prod-only Exclude dev/test dependencies
|
|
47412
|
+
--deterministic Omit timestamp / serialNumber / namespace for reproducible output
|
|
47413
|
+
-o, --output <file> Write the document to a file (default: stdout)
|
|
47414
|
+
Discovers package.json / requirements.txt / pom.xml / Cargo.toml / go.mod.
|
|
47415
|
+
CVE matching is out of scope (deterministic SAST — no network).
|
|
47416
|
+
|
|
47163
47417
|
EXAMPLES:
|
|
47164
47418
|
cognium-dev scan src/
|
|
47165
47419
|
cognium-dev scan app.java -f json -o results.json
|
|
@@ -47177,6 +47431,9 @@ EXAMPLES:
|
|
|
47177
47431
|
cognium-dev metrics src/
|
|
47178
47432
|
cognium-dev metrics src/ --category complexity
|
|
47179
47433
|
cognium-dev metrics src/ --format json --profile custom-config.json
|
|
47434
|
+
cognium-dev sbom . # CycloneDX 1.5 to stdout
|
|
47435
|
+
cognium-dev sbom . -f spdx -o sbom.spdx.json # SPDX 2.3 to a file
|
|
47436
|
+
cognium-dev sbom . --prod-only --deterministic # reproducible, no dev deps
|
|
47180
47437
|
cognium-dev list-passes
|
|
47181
47438
|
cognium-dev list-passes reliability
|
|
47182
47439
|
cognium-dev init
|
|
@@ -47882,8 +48139,8 @@ async function runScan(targetPath, options) {
|
|
|
47882
48139
|
output = formatResults(results, options.verbose, crossFileData, profileSummary);
|
|
47883
48140
|
}
|
|
47884
48141
|
if (options.output) {
|
|
47885
|
-
const { writeFileSync } = await import("fs");
|
|
47886
|
-
|
|
48142
|
+
const { writeFileSync: writeFileSync2 } = await import("fs");
|
|
48143
|
+
writeFileSync2(options.output, output);
|
|
47887
48144
|
console.error(colors.green(`Results written to ${options.output}`));
|
|
47888
48145
|
} else if (output.trim()) {
|
|
47889
48146
|
console.log(output);
|
|
@@ -48036,8 +48293,8 @@ async function runMetrics(targetPath, options) {
|
|
|
48036
48293
|
`);
|
|
48037
48294
|
}
|
|
48038
48295
|
if (options.output) {
|
|
48039
|
-
const { writeFileSync } = await import("fs");
|
|
48040
|
-
|
|
48296
|
+
const { writeFileSync: writeFileSync2 } = await import("fs");
|
|
48297
|
+
writeFileSync2(options.output, output);
|
|
48041
48298
|
console.error(colors.green(`Results written to ${options.output}`));
|
|
48042
48299
|
} else {
|
|
48043
48300
|
console.log(output);
|
|
@@ -48145,8 +48402,8 @@ async function handleInit() {
|
|
|
48145
48402
|
severity: "low",
|
|
48146
48403
|
categories: ["security", "reliability", "performance", "maintainability", "architecture"]
|
|
48147
48404
|
};
|
|
48148
|
-
const { writeFileSync } = await import("fs");
|
|
48149
|
-
|
|
48405
|
+
const { writeFileSync: writeFileSync2 } = await import("fs");
|
|
48406
|
+
writeFileSync2(configPath, JSON.stringify(config, null, 2));
|
|
48150
48407
|
console.log(colors.green(`Created ${configPath}`));
|
|
48151
48408
|
}
|
|
48152
48409
|
function applyLogLevel(cliValue) {
|
|
@@ -48192,6 +48449,92 @@ function parseCrossFileBudgetMs(raw) {
|
|
|
48192
48449
|
}
|
|
48193
48450
|
return n;
|
|
48194
48451
|
}
|
|
48452
|
+
var SBOM_MANIFESTS = {
|
|
48453
|
+
"package.json": parseNpmDependencies,
|
|
48454
|
+
"requirements.txt": parsePypiDependencies,
|
|
48455
|
+
"pom.xml": parseMavenDependencies,
|
|
48456
|
+
"Cargo.toml": parseCargoDependencies,
|
|
48457
|
+
"go.mod": parseGoDependencies
|
|
48458
|
+
};
|
|
48459
|
+
var SBOM_SKIP_DIRS = /^(node_modules|vendor|target|dist|build|out|coverage)$/;
|
|
48460
|
+
async function collectManifestFiles(targetPath) {
|
|
48461
|
+
const found = [];
|
|
48462
|
+
const pathStat = await stat2(targetPath);
|
|
48463
|
+
if (pathStat.isFile()) {
|
|
48464
|
+
if (SBOM_MANIFESTS[basename(targetPath)])
|
|
48465
|
+
found.push(targetPath);
|
|
48466
|
+
return found;
|
|
48467
|
+
}
|
|
48468
|
+
const walk2 = async (dir) => {
|
|
48469
|
+
for (const e of await readdir2(dir, { withFileTypes: true })) {
|
|
48470
|
+
if (e.name.startsWith("."))
|
|
48471
|
+
continue;
|
|
48472
|
+
if (e.isDirectory()) {
|
|
48473
|
+
if (SBOM_SKIP_DIRS.test(e.name))
|
|
48474
|
+
continue;
|
|
48475
|
+
await walk2(join2(dir, e.name));
|
|
48476
|
+
} else if (SBOM_MANIFESTS[e.name]) {
|
|
48477
|
+
found.push(join2(dir, e.name));
|
|
48478
|
+
}
|
|
48479
|
+
}
|
|
48480
|
+
};
|
|
48481
|
+
await walk2(targetPath);
|
|
48482
|
+
return found;
|
|
48483
|
+
}
|
|
48484
|
+
async function runSbom(targetPath, options) {
|
|
48485
|
+
const absPath = resolve2(targetPath);
|
|
48486
|
+
if (!existsSync(absPath)) {
|
|
48487
|
+
console.error(colors.red(`Error: path not found: ${targetPath}`));
|
|
48488
|
+
process.exit(2);
|
|
48489
|
+
}
|
|
48490
|
+
const manifests = await collectManifestFiles(absPath);
|
|
48491
|
+
if (manifests.length === 0) {
|
|
48492
|
+
console.error(colors.red("Error: no supported manifests found (package.json, requirements.txt, pom.xml, Cargo.toml, go.mod)"));
|
|
48493
|
+
process.exit(1);
|
|
48494
|
+
}
|
|
48495
|
+
let deps = [];
|
|
48496
|
+
let projectName = options.name;
|
|
48497
|
+
for (const m of manifests) {
|
|
48498
|
+
const content = readFileSync(m, "utf-8");
|
|
48499
|
+
deps.push(...SBOM_MANIFESTS[basename(m)](content));
|
|
48500
|
+
if (!projectName && basename(m) === "package.json") {
|
|
48501
|
+
try {
|
|
48502
|
+
const n = JSON.parse(content).name;
|
|
48503
|
+
if (typeof n === "string" && n)
|
|
48504
|
+
projectName = n;
|
|
48505
|
+
} catch {}
|
|
48506
|
+
}
|
|
48507
|
+
}
|
|
48508
|
+
const seen = new Set;
|
|
48509
|
+
deps = deps.filter((d) => {
|
|
48510
|
+
const key = `${d.ecosystem}|${d.name}|${d.version}|${d.scope}`;
|
|
48511
|
+
if (seen.has(key))
|
|
48512
|
+
return false;
|
|
48513
|
+
seen.add(key);
|
|
48514
|
+
return true;
|
|
48515
|
+
});
|
|
48516
|
+
if (options.prodOnly)
|
|
48517
|
+
deps = deps.filter((d) => d.scope !== "dev");
|
|
48518
|
+
if (!projectName)
|
|
48519
|
+
projectName = basename(absPath) || "project";
|
|
48520
|
+
const meta = { name: projectName, tool: "cognium-dev" };
|
|
48521
|
+
if (!options.deterministic) {
|
|
48522
|
+
meta.timestamp = new Date().toISOString();
|
|
48523
|
+
const { randomUUID } = await import("crypto");
|
|
48524
|
+
meta.serialNumber = `urn:uuid:${randomUUID()}`;
|
|
48525
|
+
meta.namespace = `https://cognium.dev/spdxdocs/${projectName}-${randomUUID()}`;
|
|
48526
|
+
}
|
|
48527
|
+
const doc = options.format === "spdx" ? toSpdx(deps, meta) : toCycloneDx(deps, meta);
|
|
48528
|
+
const out2 = JSON.stringify(doc, null, 2);
|
|
48529
|
+
if (options.output) {
|
|
48530
|
+
writeFileSync(options.output, out2);
|
|
48531
|
+
console.error(colors.green(`SBOM written to ${options.output} — ${deps.length} dependencies from ${manifests.length} manifest(s)`));
|
|
48532
|
+
} else {
|
|
48533
|
+
console.log(out2);
|
|
48534
|
+
console.error(colors.green(`SBOM: ${deps.length} dependencies from ${manifests.length} manifest(s)`));
|
|
48535
|
+
}
|
|
48536
|
+
process.exit(0);
|
|
48537
|
+
}
|
|
48195
48538
|
async function main() {
|
|
48196
48539
|
const { command, args: args2, options } = parseArgs(process.argv.slice(2));
|
|
48197
48540
|
applyLogLevel(options["log-level"]);
|
|
@@ -48232,6 +48575,28 @@ Usage: cognium-dev metrics <path> [options]`);
|
|
|
48232
48575
|
await runMetrics(targetPath, metricsOptions);
|
|
48233
48576
|
return;
|
|
48234
48577
|
}
|
|
48578
|
+
if (command === "sbom") {
|
|
48579
|
+
if (args2.length === 0) {
|
|
48580
|
+
console.error(colors.red("Error: sbom command requires a path argument"));
|
|
48581
|
+
console.error(`
|
|
48582
|
+
Usage: cognium-dev sbom <path> [--format cyclonedx|spdx] [--output <file>]`);
|
|
48583
|
+
process.exit(1);
|
|
48584
|
+
}
|
|
48585
|
+
const rawFormat = (options.format || options.f || "cyclonedx").toLowerCase();
|
|
48586
|
+
if (rawFormat !== "cyclonedx" && rawFormat !== "spdx") {
|
|
48587
|
+
console.error(colors.red(`Error: unknown SBOM format '${rawFormat}' (expected 'cyclonedx' or 'spdx')`));
|
|
48588
|
+
process.exit(1);
|
|
48589
|
+
}
|
|
48590
|
+
const sbomOptions = {
|
|
48591
|
+
format: rawFormat,
|
|
48592
|
+
output: options.output || options.o,
|
|
48593
|
+
name: options.name,
|
|
48594
|
+
prodOnly: options["prod-only"] === true || options.prod === true,
|
|
48595
|
+
deterministic: options.deterministic === true
|
|
48596
|
+
};
|
|
48597
|
+
await runSbom(args2[0], sbomOptions);
|
|
48598
|
+
return;
|
|
48599
|
+
}
|
|
48235
48600
|
if (command === "scan") {
|
|
48236
48601
|
if (args2.length === 0) {
|
|
48237
48602
|
console.error(colors.red("Error: scan command requires a path argument"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.202.0",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@cognium/project-profile-detect": "^1.1.0",
|
|
69
|
-
"circle-ir": "^3.
|
|
69
|
+
"circle-ir": "^3.202.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|