@cyclonedx/cdxgen 8.1.8 → 8.2.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 +2 -0
- package/docker.js +18 -2
- package/index.js +139 -5
- package/package.json +1 -1
- package/server.js +15 -1
- package/utils.js +204 -3
- package/utils.test.js +264 -89
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ When used with plugins, cdxgen could generate an SBoM for Linux docker images an
|
|
|
23
23
|
| elixir | mix.lock | Yes |
|
|
24
24
|
| c/c++ | conan.lock, conanfile.txt | Yes only for conan.lock |
|
|
25
25
|
| clojure | Clojure CLI (deps.edn), Leiningen (project.clj) | Yes unless the files are parsed manually due to lack of clojure cli or leiningen command |
|
|
26
|
+
| swift | Package.resolved, Package.swift (swiftpm) | Yes |
|
|
26
27
|
| docker / oci image | All supported languages. Linux OS packages with plugins [4] | Best effort based on lock files |
|
|
27
28
|
| GitHub Actions | .github/workflows/\*.yml | N/A |
|
|
28
29
|
| Linux | All supported languages. Linux OS packages with plugins [5] | Best effort based on lock files |
|
|
@@ -262,6 +263,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
|
|
|
262
263
|
| FETCH_LICENSE | Set this variable to fetch license information from the registry. npm and golang only |
|
|
263
264
|
| USE_GOSUM | Set to true to generate BOMs for golang projects using go.sum as the dependency source of truth, instead of go.mod |
|
|
264
265
|
| CDXGEN_TIMEOUT_MS | Default timeout for known execution involving maven, gradle or sbt |
|
|
266
|
+
| CDXGEN_SERVER_TIMEOUT_MS | Default timeout in server mode |
|
|
265
267
|
| BAZEL_TARGET | Bazel target to build. Default :all (Eg: //java-maven) |
|
|
266
268
|
| CLJ_CMD | Set to override the clojure cli command |
|
|
267
269
|
| LEIN_CMD | Set to override the leiningen command |
|
package/docker.js
CHANGED
|
@@ -117,7 +117,11 @@ const getDefaultOptions = () => {
|
|
|
117
117
|
? "npipe//./pipe/docker_engine:"
|
|
118
118
|
: "unix:/var/run/docker.sock:";
|
|
119
119
|
*/
|
|
120
|
-
opts.prefixUrl = isWin
|
|
120
|
+
opts.prefixUrl = isWin
|
|
121
|
+
? WIN_LOCAL_TLS
|
|
122
|
+
: isDockerRootless
|
|
123
|
+
? `unix:${os.homedir()}/.docker/run/docker.sock:`
|
|
124
|
+
: "unix:/var/run/docker.sock:";
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
} else {
|
|
@@ -162,6 +166,18 @@ const getConnection = async (options) => {
|
|
|
162
166
|
}
|
|
163
167
|
} catch (err) {
|
|
164
168
|
// console.log(err, opts);
|
|
169
|
+
opts.prefixUrl = `unix:${os.homedir()}/.docker/run/docker.sock:`;
|
|
170
|
+
try {
|
|
171
|
+
await got.get("_ping", opts);
|
|
172
|
+
dockerConn = got.extend(opts);
|
|
173
|
+
isDockerRootless = true;
|
|
174
|
+
if (DEBUG_MODE) {
|
|
175
|
+
console.log("Docker service in rootless mode detected!");
|
|
176
|
+
}
|
|
177
|
+
return dockerConn;
|
|
178
|
+
} catch (err) {
|
|
179
|
+
// console.log(err, opts);
|
|
180
|
+
}
|
|
165
181
|
try {
|
|
166
182
|
if (isWin) {
|
|
167
183
|
opts.prefixUrl = WIN_LOCAL_TLS;
|
|
@@ -323,7 +339,7 @@ const getImage = async (fullImageName) => {
|
|
|
323
339
|
}
|
|
324
340
|
try {
|
|
325
341
|
localData = await makeRequest(`images/${repo}/json`);
|
|
326
|
-
if (DEBUG_MODE) {
|
|
342
|
+
if (DEBUG_MODE && localData) {
|
|
327
343
|
console.log(localData);
|
|
328
344
|
}
|
|
329
345
|
} catch (err) {
|
package/index.js
CHANGED
|
@@ -44,6 +44,11 @@ if (process.env.PIP_CMD) {
|
|
|
44
44
|
PIP_CMD = process.env.PIP_CMD;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
let SWIFT_CMD = "swift";
|
|
48
|
+
if (process.env.SWIFT_CMD) {
|
|
49
|
+
SWIFT_CMD = process.env.SWIFT_CMD;
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
// Construct sbt cache directory
|
|
48
53
|
let SBT_CACHE_DIR =
|
|
49
54
|
process.env.SBT_CACHE_DIR || pathLib.join(os.homedir(), ".ivy2", "cache");
|
|
@@ -61,6 +66,29 @@ const HASH_PATTERN =
|
|
|
61
66
|
// Timeout milliseconds. Default 10 mins
|
|
62
67
|
const TIMEOUT_MS = parseInt(process.env.CDXGEN_TIMEOUT_MS) || 10 * 60 * 1000;
|
|
63
68
|
|
|
69
|
+
const createDefaultParentComponent = (path) => {
|
|
70
|
+
// Create a parent component based on the directory name
|
|
71
|
+
let dirName = pathLib.dirname(path);
|
|
72
|
+
const tmpA = dirName.split(pathLib.sep);
|
|
73
|
+
dirName = tmpA[tmpA.length - 1];
|
|
74
|
+
const parentComponent = {
|
|
75
|
+
group: "",
|
|
76
|
+
name: dirName,
|
|
77
|
+
type: "application"
|
|
78
|
+
};
|
|
79
|
+
const ppurl = new PackageURL(
|
|
80
|
+
"application",
|
|
81
|
+
parentComponent.group,
|
|
82
|
+
parentComponent.name,
|
|
83
|
+
parentComponent.version,
|
|
84
|
+
null,
|
|
85
|
+
null
|
|
86
|
+
).toString();
|
|
87
|
+
parentComponent["bom-ref"] = ppurl;
|
|
88
|
+
parentComponent["purl"] = ppurl;
|
|
89
|
+
return parentComponent;
|
|
90
|
+
};
|
|
91
|
+
|
|
64
92
|
const determineParentComponent = (options) => {
|
|
65
93
|
let parentComponent = undefined;
|
|
66
94
|
if (options.projectName && options.projectVersion) {
|
|
@@ -1008,13 +1036,13 @@ const createJavaBom = async (path, options) => {
|
|
|
1008
1036
|
"Resolve the above maven error. This could be due to the following:\n"
|
|
1009
1037
|
);
|
|
1010
1038
|
console.log(
|
|
1011
|
-
"1. Java version requirement
|
|
1039
|
+
"1. Java version requirement: cdxgen container image bundles Java 17 with gradle 8 which might be incompatible."
|
|
1012
1040
|
);
|
|
1013
1041
|
console.log(
|
|
1014
|
-
"2. Private
|
|
1042
|
+
"2. Private dependencies cannot be downloaded: Check if any additional arguments must be passed to maven and set them via MVN_ARGS environment variable."
|
|
1015
1043
|
);
|
|
1016
1044
|
console.log(
|
|
1017
|
-
"3. Check if all required environment variables including any maven profile arguments are passed correctly to this tool"
|
|
1045
|
+
"3. Check if all required environment variables including any maven profile arguments are passed correctly to this tool."
|
|
1018
1046
|
);
|
|
1019
1047
|
// Do not fall back to methods that can produce incomplete results when failOnError is set
|
|
1020
1048
|
options.failOnError && process.exit(1);
|
|
@@ -1125,7 +1153,7 @@ const createJavaBom = async (path, options) => {
|
|
|
1125
1153
|
console.error(result.stdout, result.stderr);
|
|
1126
1154
|
}
|
|
1127
1155
|
console.log(
|
|
1128
|
-
"1. Check if the correct version of java and gradle are installed and available in PATH. For example, some project might require Java 11 with gradle 7."
|
|
1156
|
+
"1. Check if the correct version of java and gradle are installed and available in PATH. For example, some project might require Java 11 with gradle 7.\n cdxgen container image bundles Java 17 with gradle 8 which might be incompatible."
|
|
1129
1157
|
);
|
|
1130
1158
|
options.failOnError && process.exit(1);
|
|
1131
1159
|
}
|
|
@@ -1251,7 +1279,7 @@ const createJavaBom = async (path, options) => {
|
|
|
1251
1279
|
}
|
|
1252
1280
|
if (DEBUG_MODE || !result.stderr || options.failOnError) {
|
|
1253
1281
|
console.log(
|
|
1254
|
-
"1. Check if the correct version of java and gradle are installed and available in PATH. For example, some project might require Java 11 with gradle 7."
|
|
1282
|
+
"1. Check if the correct version of java and gradle are installed and available in PATH. For example, some project might require Java 11 with gradle 7.\n cdxgen container image bundles Java 17 with gradle 8 which might be incompatible."
|
|
1255
1283
|
);
|
|
1256
1284
|
console.log(
|
|
1257
1285
|
"2. When using tools such as sdkman, the init script must be invoked to set the PATH variables correctly."
|
|
@@ -2805,6 +2833,91 @@ const createHelmBom = async (path, options) => {
|
|
|
2805
2833
|
return {};
|
|
2806
2834
|
};
|
|
2807
2835
|
|
|
2836
|
+
/**
|
|
2837
|
+
* Function to create bom string for swift projects
|
|
2838
|
+
*
|
|
2839
|
+
* @param path to the project
|
|
2840
|
+
* @param options Parse options from the cli
|
|
2841
|
+
*/
|
|
2842
|
+
const createSwiftBom = async (path, options) => {
|
|
2843
|
+
const swiftFiles = utils.getAllFiles(
|
|
2844
|
+
path,
|
|
2845
|
+
(options.multiProject ? "**/" : "") + "Package*.swift"
|
|
2846
|
+
);
|
|
2847
|
+
const pkgResolvedFiles = utils.getAllFiles(
|
|
2848
|
+
path,
|
|
2849
|
+
(options.multiProject ? "**/" : "") + "Package.resolved"
|
|
2850
|
+
);
|
|
2851
|
+
let pkgList = [];
|
|
2852
|
+
let dependencies = [];
|
|
2853
|
+
let parentComponent = {};
|
|
2854
|
+
let completedPath = [];
|
|
2855
|
+
if (pkgResolvedFiles.length) {
|
|
2856
|
+
for (let f of pkgResolvedFiles) {
|
|
2857
|
+
if (!parentComponent || !Object.keys(parentComponent).length) {
|
|
2858
|
+
parentComponent = createDefaultParentComponent(f);
|
|
2859
|
+
}
|
|
2860
|
+
if (DEBUG_MODE) {
|
|
2861
|
+
console.log("Parsing", f);
|
|
2862
|
+
}
|
|
2863
|
+
const dlist = utils.parseSwiftResolved(f);
|
|
2864
|
+
if (dlist && dlist.length) {
|
|
2865
|
+
pkgList = pkgList.concat(dlist);
|
|
2866
|
+
}
|
|
2867
|
+
}
|
|
2868
|
+
} else if (swiftFiles.length) {
|
|
2869
|
+
for (let f of swiftFiles) {
|
|
2870
|
+
const basePath = pathLib.dirname(f);
|
|
2871
|
+
if (completedPath.includes(basePath)) {
|
|
2872
|
+
continue;
|
|
2873
|
+
}
|
|
2874
|
+
let treeData = undefined;
|
|
2875
|
+
if (DEBUG_MODE) {
|
|
2876
|
+
console.log("Executing 'swift package show-dependencies' in", basePath);
|
|
2877
|
+
}
|
|
2878
|
+
const result = spawnSync(
|
|
2879
|
+
SWIFT_CMD,
|
|
2880
|
+
["package", "show-dependencies", "--format", "json"],
|
|
2881
|
+
{
|
|
2882
|
+
cwd: basePath,
|
|
2883
|
+
encoding: "utf-8",
|
|
2884
|
+
timeout: TIMEOUT_MS
|
|
2885
|
+
}
|
|
2886
|
+
);
|
|
2887
|
+
if (result.status === 0 && result.stdout) {
|
|
2888
|
+
completedPath.push(basePath);
|
|
2889
|
+
treeData = Buffer.from(result.stdout).toString();
|
|
2890
|
+
const retData = utils.parseSwiftJsonTree(treeData, f);
|
|
2891
|
+
if (retData.pkgList && retData.pkgList.length) {
|
|
2892
|
+
parentComponent = retData.pkgList.splice(0, 1)[0];
|
|
2893
|
+
parentComponent.type = "application";
|
|
2894
|
+
pkgList = pkgList.concat(retData.pkgList);
|
|
2895
|
+
}
|
|
2896
|
+
if (retData.dependenciesList) {
|
|
2897
|
+
dependencies = mergeDependencies(
|
|
2898
|
+
dependencies,
|
|
2899
|
+
retData.dependenciesList
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
} else {
|
|
2903
|
+
if (DEBUG_MODE) {
|
|
2904
|
+
console.log(
|
|
2905
|
+
"Please install swift from https://www.swift.org/download/ or use the cdxgen container image"
|
|
2906
|
+
);
|
|
2907
|
+
}
|
|
2908
|
+
console.error(result.stderr);
|
|
2909
|
+
options.failOnError && process.exit(1);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
return buildBomNSData(options, pkgList, "swift", {
|
|
2914
|
+
src: path,
|
|
2915
|
+
filename: swiftFiles.join(", "),
|
|
2916
|
+
parentComponent,
|
|
2917
|
+
dependencies
|
|
2918
|
+
});
|
|
2919
|
+
};
|
|
2920
|
+
|
|
2808
2921
|
/**
|
|
2809
2922
|
* Function to create bom string for docker compose
|
|
2810
2923
|
*
|
|
@@ -4041,6 +4154,19 @@ const createXBom = async (path, options) => {
|
|
|
4041
4154
|
if (cbFiles.length) {
|
|
4042
4155
|
return await createCloudBuildBom(path, options);
|
|
4043
4156
|
}
|
|
4157
|
+
|
|
4158
|
+
// Swift
|
|
4159
|
+
const swiftFiles = utils.getAllFiles(
|
|
4160
|
+
path,
|
|
4161
|
+
(options.multiProject ? "**/" : "") + "Package*.swift"
|
|
4162
|
+
);
|
|
4163
|
+
const pkgResolvedFiles = utils.getAllFiles(
|
|
4164
|
+
path,
|
|
4165
|
+
(options.multiProject ? "**/" : "") + "Package.resolved"
|
|
4166
|
+
);
|
|
4167
|
+
if (swiftFiles.length || pkgResolvedFiles.length) {
|
|
4168
|
+
return await createSwiftBom(path, options);
|
|
4169
|
+
}
|
|
4044
4170
|
};
|
|
4045
4171
|
|
|
4046
4172
|
/**
|
|
@@ -4167,6 +4293,10 @@ const createBom = async (path, options) => {
|
|
|
4167
4293
|
case "kotlin":
|
|
4168
4294
|
case "scala":
|
|
4169
4295
|
case "jvm":
|
|
4296
|
+
case "gradle":
|
|
4297
|
+
case "mvn":
|
|
4298
|
+
case "maven":
|
|
4299
|
+
case "sbt":
|
|
4170
4300
|
return await createJavaBom(path, options);
|
|
4171
4301
|
case "jar":
|
|
4172
4302
|
options.multiProject = true;
|
|
@@ -4192,6 +4322,7 @@ const createBom = async (path, options) => {
|
|
|
4192
4322
|
case "javascript":
|
|
4193
4323
|
case "typescript":
|
|
4194
4324
|
case "ts":
|
|
4325
|
+
case "tsx":
|
|
4195
4326
|
return await createNodejsBom(path, options);
|
|
4196
4327
|
case "python":
|
|
4197
4328
|
case "py":
|
|
@@ -4282,6 +4413,9 @@ const createBom = async (path, options) => {
|
|
|
4282
4413
|
case "cloudbuild":
|
|
4283
4414
|
options.multiProject = true;
|
|
4284
4415
|
return await createCloudBuildBom(path, options);
|
|
4416
|
+
case "swift":
|
|
4417
|
+
options.multiProject = true;
|
|
4418
|
+
return await createSwiftBom(path, options);
|
|
4285
4419
|
default:
|
|
4286
4420
|
// In recurse mode return multi-language Bom
|
|
4287
4421
|
// https://github.com/cyclonedx/cdxgen/issues/95
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyclonedx/cdxgen",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.0",
|
|
4
4
|
"description": "Creates CycloneDX Software Bill-of-Materials (SBOM) from source or container image",
|
|
5
5
|
"homepage": "http://github.com/cyclonedx/cdxgen",
|
|
6
6
|
"author": "Prabhu Subramanian <prabhu@appthreat.com>",
|
package/server.js
CHANGED
|
@@ -9,6 +9,10 @@ const path = require("path");
|
|
|
9
9
|
const bom = require("./index.js");
|
|
10
10
|
const compression = require("compression");
|
|
11
11
|
|
|
12
|
+
// Timeout milliseconds. Default 10 mins
|
|
13
|
+
const TIMEOUT_MS =
|
|
14
|
+
parseInt(process.env.CDXGEN_SERVER_TIMEOUT_MS) || 10 * 60 * 1000;
|
|
15
|
+
|
|
12
16
|
const app = connect();
|
|
13
17
|
|
|
14
18
|
app.use(
|
|
@@ -68,9 +72,19 @@ const parseQueryString = (q, body, options = {}) => {
|
|
|
68
72
|
return options;
|
|
69
73
|
};
|
|
70
74
|
|
|
75
|
+
const configureServer = (cdxgenServer) => {
|
|
76
|
+
cdxgenServer.headersTimeout = TIMEOUT_MS;
|
|
77
|
+
cdxgenServer.requestTimeout = TIMEOUT_MS;
|
|
78
|
+
cdxgenServer.timeout = 0;
|
|
79
|
+
cdxgenServer.keepAliveTimeout = 0;
|
|
80
|
+
};
|
|
81
|
+
|
|
71
82
|
const start = async (options) => {
|
|
72
83
|
console.log("Listening on", options.serverHost, options.serverPort);
|
|
73
|
-
http
|
|
84
|
+
const cdxgenServer = http
|
|
85
|
+
.createServer(app)
|
|
86
|
+
.listen(options.serverPort, options.serverHost);
|
|
87
|
+
configureServer(cdxgenServer);
|
|
74
88
|
app.use("/sbom", async function (req, res) {
|
|
75
89
|
const q = url.parse(req.url, true).query;
|
|
76
90
|
let cleanup = false;
|
package/utils.js
CHANGED
|
@@ -377,7 +377,7 @@ const parsePkgLock = async (pkgLockFile) => {
|
|
|
377
377
|
type: "application"
|
|
378
378
|
};
|
|
379
379
|
}
|
|
380
|
-
if (rootPkg) {
|
|
380
|
+
if (rootPkg && rootPkg.name) {
|
|
381
381
|
const purl = new PackageURL(
|
|
382
382
|
"application",
|
|
383
383
|
"",
|
|
@@ -1018,7 +1018,7 @@ exports.parsePom = parsePom;
|
|
|
1018
1018
|
*/
|
|
1019
1019
|
const parseMavenTree = function (rawOutput) {
|
|
1020
1020
|
if (!rawOutput) {
|
|
1021
|
-
return
|
|
1021
|
+
return {};
|
|
1022
1022
|
}
|
|
1023
1023
|
const deps = [];
|
|
1024
1024
|
const dependenciesList = [];
|
|
@@ -1128,7 +1128,7 @@ const parseGradleDep = function (rawOutput) {
|
|
|
1128
1128
|
level_trees[last_purl] = [];
|
|
1129
1129
|
let stack = [last_purl];
|
|
1130
1130
|
const depRegex =
|
|
1131
|
-
/^.*?--- +(?<group>[^\s:]+):(?<name>[^\s:]+)(?::(?:{strictly )?(?<versionspecified>[
|
|
1131
|
+
/^.*?--- +(?<group>[^\s:]+):(?<name>[^\s:]+)(?::(?:{strictly [[]?)?(?<versionspecified>[^,\s:}]+))?(?:})?(?:[^->]* +-> +(?<versionoverride>[^\s:]+))?/gm;
|
|
1132
1132
|
while ((match = depRegex.exec(rawOutput))) {
|
|
1133
1133
|
const [line, group, name, versionspecified, versionoverride] = match;
|
|
1134
1134
|
const version = versionoverride || versionspecified;
|
|
@@ -3773,6 +3773,207 @@ const convertOSQueryResults = function (queryCategory, queryObj, results) {
|
|
|
3773
3773
|
};
|
|
3774
3774
|
exports.convertOSQueryResults = convertOSQueryResults;
|
|
3775
3775
|
|
|
3776
|
+
const _swiftDepPkgList = (pkgList, dependenciesList, depKeys, jsonData) => {
|
|
3777
|
+
if (jsonData && jsonData.dependencies) {
|
|
3778
|
+
for (let adep of jsonData.dependencies) {
|
|
3779
|
+
const urlOrPath = adep.url || adep.path;
|
|
3780
|
+
const apkg = {
|
|
3781
|
+
group: adep.identity || "",
|
|
3782
|
+
name: adep.name,
|
|
3783
|
+
version: adep.version
|
|
3784
|
+
};
|
|
3785
|
+
const purl = new PackageURL(
|
|
3786
|
+
"swift",
|
|
3787
|
+
apkg.group,
|
|
3788
|
+
apkg.name,
|
|
3789
|
+
apkg.version,
|
|
3790
|
+
null,
|
|
3791
|
+
null
|
|
3792
|
+
);
|
|
3793
|
+
const purlString = decodeURIComponent(purl.toString());
|
|
3794
|
+
if (urlOrPath) {
|
|
3795
|
+
if (urlOrPath.startsWith("http")) {
|
|
3796
|
+
apkg.repository = { url: urlOrPath };
|
|
3797
|
+
if (apkg.path) {
|
|
3798
|
+
apkg.properties = [
|
|
3799
|
+
{
|
|
3800
|
+
name: "SrcPath",
|
|
3801
|
+
value: apkg.path
|
|
3802
|
+
}
|
|
3803
|
+
];
|
|
3804
|
+
}
|
|
3805
|
+
} else {
|
|
3806
|
+
apkg.properties = [
|
|
3807
|
+
{
|
|
3808
|
+
name: "SrcPath",
|
|
3809
|
+
value: urlOrPath
|
|
3810
|
+
}
|
|
3811
|
+
];
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
pkgList.push(apkg);
|
|
3815
|
+
// Handle the immediate dependencies before recursing
|
|
3816
|
+
if (adep.dependencies && adep.dependencies.length) {
|
|
3817
|
+
const deplist = [];
|
|
3818
|
+
for (let cdep of adep.dependencies) {
|
|
3819
|
+
const deppurl = new PackageURL(
|
|
3820
|
+
"swift",
|
|
3821
|
+
cdep.identity || "",
|
|
3822
|
+
cdep.name,
|
|
3823
|
+
cdep.version,
|
|
3824
|
+
null,
|
|
3825
|
+
null
|
|
3826
|
+
);
|
|
3827
|
+
const deppurlString = decodeURIComponent(deppurl.toString());
|
|
3828
|
+
deplist.push(deppurlString);
|
|
3829
|
+
}
|
|
3830
|
+
if (!depKeys[purlString]) {
|
|
3831
|
+
dependenciesList.push({
|
|
3832
|
+
ref: purlString,
|
|
3833
|
+
dependsOn: deplist
|
|
3834
|
+
});
|
|
3835
|
+
depKeys[purlString] = true;
|
|
3836
|
+
}
|
|
3837
|
+
if (adep.dependencies && adep.dependencies.length) {
|
|
3838
|
+
_swiftDepPkgList(pkgList, dependenciesList, depKeys, adep);
|
|
3839
|
+
}
|
|
3840
|
+
} else {
|
|
3841
|
+
if (!depKeys[purlString]) {
|
|
3842
|
+
dependenciesList.push({
|
|
3843
|
+
ref: purlString,
|
|
3844
|
+
dependsOn: []
|
|
3845
|
+
});
|
|
3846
|
+
depKeys[purlString] = true;
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
return { pkgList, dependenciesList };
|
|
3852
|
+
};
|
|
3853
|
+
|
|
3854
|
+
/**
|
|
3855
|
+
* Parse swift dependency tree output
|
|
3856
|
+
* @param {string} rawOutput Swift dependencies json output
|
|
3857
|
+
* @param {string} pkgFile Package.swift file
|
|
3858
|
+
*/
|
|
3859
|
+
const parseSwiftJsonTree = (rawOutput, pkgFile) => {
|
|
3860
|
+
if (!rawOutput) {
|
|
3861
|
+
return {};
|
|
3862
|
+
}
|
|
3863
|
+
const pkgList = [];
|
|
3864
|
+
const dependenciesList = [];
|
|
3865
|
+
let depKeys = {};
|
|
3866
|
+
let rootPkg = {};
|
|
3867
|
+
let jsonData = {};
|
|
3868
|
+
try {
|
|
3869
|
+
jsonData = JSON.parse(rawOutput);
|
|
3870
|
+
if (jsonData && jsonData.name) {
|
|
3871
|
+
rootPkg = {
|
|
3872
|
+
group: jsonData.identity || "",
|
|
3873
|
+
name: jsonData.name,
|
|
3874
|
+
version: jsonData.version
|
|
3875
|
+
};
|
|
3876
|
+
const urlOrPath = jsonData.url || jsonData.path;
|
|
3877
|
+
if (urlOrPath) {
|
|
3878
|
+
if (urlOrPath.startsWith("http")) {
|
|
3879
|
+
rootPkg.repository = { url: urlOrPath };
|
|
3880
|
+
} else {
|
|
3881
|
+
rootPkg.properties = [
|
|
3882
|
+
{
|
|
3883
|
+
name: "SrcPath",
|
|
3884
|
+
value: urlOrPath
|
|
3885
|
+
},
|
|
3886
|
+
{
|
|
3887
|
+
name: "SrcFile",
|
|
3888
|
+
value: pkgFile
|
|
3889
|
+
}
|
|
3890
|
+
];
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
const purl = new PackageURL(
|
|
3894
|
+
"application",
|
|
3895
|
+
rootPkg.group,
|
|
3896
|
+
rootPkg.name,
|
|
3897
|
+
rootPkg.version,
|
|
3898
|
+
null,
|
|
3899
|
+
null
|
|
3900
|
+
);
|
|
3901
|
+
const purlString = decodeURIComponent(purl.toString());
|
|
3902
|
+
rootPkg["bom-ref"] = purlString;
|
|
3903
|
+
pkgList.push(rootPkg);
|
|
3904
|
+
const deplist = [];
|
|
3905
|
+
for (const rd of jsonData.dependencies) {
|
|
3906
|
+
const deppurl = new PackageURL(
|
|
3907
|
+
"swift",
|
|
3908
|
+
rd.identity || "",
|
|
3909
|
+
rd.name,
|
|
3910
|
+
rd.version,
|
|
3911
|
+
null,
|
|
3912
|
+
null
|
|
3913
|
+
);
|
|
3914
|
+
const deppurlString = decodeURIComponent(deppurl.toString());
|
|
3915
|
+
deplist.push(deppurlString);
|
|
3916
|
+
}
|
|
3917
|
+
dependenciesList.push({
|
|
3918
|
+
ref: purlString,
|
|
3919
|
+
dependsOn: deplist
|
|
3920
|
+
});
|
|
3921
|
+
_swiftDepPkgList(pkgList, dependenciesList, depKeys, jsonData);
|
|
3922
|
+
}
|
|
3923
|
+
} catch (e) {
|
|
3924
|
+
if (DEBUG_MODE) {
|
|
3925
|
+
console.log(e);
|
|
3926
|
+
}
|
|
3927
|
+
return {};
|
|
3928
|
+
}
|
|
3929
|
+
return {
|
|
3930
|
+
pkgList,
|
|
3931
|
+
dependenciesList
|
|
3932
|
+
};
|
|
3933
|
+
};
|
|
3934
|
+
exports.parseSwiftJsonTree = parseSwiftJsonTree;
|
|
3935
|
+
|
|
3936
|
+
/**
|
|
3937
|
+
* Parse swift package resolved file
|
|
3938
|
+
* @param {string} resolvedFile Package.resolved file
|
|
3939
|
+
*/
|
|
3940
|
+
const parseSwiftResolved = (resolvedFile) => {
|
|
3941
|
+
const pkgList = [];
|
|
3942
|
+
if (fs.existsSync(resolvedFile)) {
|
|
3943
|
+
try {
|
|
3944
|
+
const pkgData = JSON.parse(fs.readFileSync(resolvedFile, "utf8"));
|
|
3945
|
+
let resolvedList = [];
|
|
3946
|
+
if (pkgData.pins) {
|
|
3947
|
+
resolvedList = pkgData.pins;
|
|
3948
|
+
} else if (pkgData.object && pkgData.object.pins) {
|
|
3949
|
+
resolvedList = pkgData.object.pins;
|
|
3950
|
+
}
|
|
3951
|
+
for (const adep of resolvedList) {
|
|
3952
|
+
const apkg = {
|
|
3953
|
+
name: adep.package || adep.identity,
|
|
3954
|
+
group: "",
|
|
3955
|
+
version: adep.state.version || adep.state.revision,
|
|
3956
|
+
properties: [
|
|
3957
|
+
{
|
|
3958
|
+
name: "SrcFile",
|
|
3959
|
+
value: resolvedFile
|
|
3960
|
+
}
|
|
3961
|
+
]
|
|
3962
|
+
};
|
|
3963
|
+
const repLocation = adep.location || adep.repositoryURL;
|
|
3964
|
+
if (repLocation) {
|
|
3965
|
+
apkg.repository = { url: repLocation };
|
|
3966
|
+
}
|
|
3967
|
+
pkgList.push(apkg);
|
|
3968
|
+
}
|
|
3969
|
+
} catch (err) {
|
|
3970
|
+
// continue regardless of error
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
return pkgList;
|
|
3974
|
+
};
|
|
3975
|
+
exports.parseSwiftResolved = parseSwiftResolved;
|
|
3976
|
+
|
|
3776
3977
|
/**
|
|
3777
3978
|
* Collect maven dependencies
|
|
3778
3979
|
*
|