@cyclonedx/cyclonedx-esbuild
Version:
Creates CycloneDX Software Bill of Materials (SBoM) from esbuild projects
173 lines (168 loc) • 7.6 kB
JavaScript
;
/*!
This file is part of CycloneDX generator for esbuild.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
SPDX-License-Identifier: Apache-2.0
Copyright (c) OWASP Foundation. All Rights Reserved.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.BomBuilder = void 0;
const node_path_1 = require("node:path");
const CDX = __importStar(require("@cyclonedx/cyclonedx-library"));
const _helpers_1 = require("./_helpers");
const logger_1 = require("./logger");
class BomBuilder {
componentBuilder;
purlFactory;
leGatherer;
constructor(componentBuilder, purlFactory, leFetcher) {
this.componentBuilder = componentBuilder;
this.purlFactory = purlFactory;
this.leGatherer = leFetcher;
}
fromMetafile(metafile, buildWorkingDir, collectEvidence, logger) {
logger.debug(logger_1.LogPrefixes.DEBUG, `metafile:`, metafile);
const bom = new CDX.Models.Bom();
logger.info(logger_1.LogPrefixes.INFO, 'generating components...');
const components = this.generateComponents(buildWorkingDir, metafile, collectEvidence, logger);
const rcPath = (0, _helpers_1.getPackageDescription)(buildWorkingDir)?.path
?? buildWorkingDir;
const mainComponent = components.get(rcPath);
if (undefined !== mainComponent) {
logger.debug(logger_1.LogPrefixes.DEBUG, 'set bom.metadata.component', mainComponent);
bom.metadata.component = mainComponent;
components.delete(rcPath);
}
for (const component of new Set(components.values())) {
logger.debug(logger_1.LogPrefixes.DEBUG, `add to bom.components`, component);
bom.components.add(component);
}
return bom;
}
*getLicenseEvidence(packageDir, logger) {
const files = this.leGatherer.getFileAttachments(packageDir, (error) => {
logger.info(logger_1.LogPrefixes.INFO, error.message);
logger.debug(logger_1.LogPrefixes.DEBUG, error.message, error);
});
try {
for (const { file, text } of files) {
yield new CDX.Models.NamedLicense(`file: ${file}`, { text });
}
}
catch (e) {
logger.warn(logger_1.LogPrefixes.WARN, 'collecting license evidence in', packageDir, 'failed:', e);
}
}
generateComponents(rootDir, metafile, collectEvidence, logger) {
const pkgs = new Map;
const components = new Map;
const modulePaths = new Set();
for (const { inputs, entryPoint } of Object.values(metafile.outputs)) {
if (entryPoint !== undefined) {
modulePaths.add(entryPoint);
}
for (const [filePath, { bytesInOutput }] of Object.entries(inputs)) {
if (bytesInOutput > 0) {
modulePaths.add(filePath);
}
}
}
logger.debug(logger_1.LogPrefixes.DEBUG, `used modulePaths:`, modulePaths);
logger.info(logger_1.LogPrefixes.INFO, 'start building Components from modules...');
for (const modulePath of modulePaths) {
const pkg = (0, _helpers_1.getPackageDescription)((0, node_path_1.resolve)(rootDir, modulePath));
if (pkg === undefined) {
logger.debug('skipped package for', modulePath);
continue;
}
let component = pkgs.get(pkg.path);
if (component === undefined) {
logger.info(logger_1.LogPrefixes.INFO, 'try to build new Component from PkgPath:', pkg.path);
try {
component = this.makeComponent(pkg, collectEvidence, logger);
}
catch (err) {
logger.debug(logger_1.LogPrefixes.DEBUG, 'unexpected error:', err);
logger.warn(logger_1.LogPrefixes.WARN, 'skipped Component from PkgPath', pkg.path);
continue;
}
logger.debug(logger_1.LogPrefixes.DEBUG, 'built', component, 'based on', pkg, 'for modulePaths', modulePaths);
pkgs.set(pkg.path, component);
}
components.set(modulePath, component);
}
logger.info(logger_1.LogPrefixes.INFO, `linking Component.dependencies...`);
this.linkDependencies(metafile, components);
logger.info(logger_1.LogPrefixes.INFO, 'done building Components from modules...');
return pkgs;
}
linkDependencies(metafile, modulesComponents) {
}
makeComponent(pkg, collectEvidence, logger) {
try {
const _packageJson = structuredClone(pkg.packageJson);
(0, _helpers_1.normalizePackageManifest)(_packageJson);
pkg.packageJson = _packageJson;
}
catch (e) {
logger.warn(logger_1.LogPrefixes.WARN, 'normalizePackageJson from PkgPath', pkg.path, 'failed:', e);
}
const component = this.componentBuilder.makeComponent(pkg.packageJson);
if (component === undefined) {
throw new Error(`failed building Component from PkgPath ${pkg.path}`);
}
component.licenses.forEach(l => {
l.acknowledgement = CDX.Enums.LicenseAcknowledgement.Declared;
});
if (collectEvidence) {
component.evidence = new CDX.Models.ComponentEvidence({
licenses: new CDX.Models.LicenseRepository(this.getLicenseEvidence((0, node_path_1.dirname)(pkg.path), logger))
});
}
component.purl = this.purlFactory.makeFromComponent(component);
component.bomRef.value = component.purl?.toString();
return component;
}
}
exports.BomBuilder = BomBuilder;