Skip to content

[WIP] generate SBOM from "global" env #503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cyclonedx/cyclonedx-npm",
"version": "1.7.3",
"version": "1.8.0-alpha.5d82149ae21396f6824c94185281b9162e2a1841",
"description": "Create CycloneDX Software Bill of Materials (SBOM) from NPM projects.",
"keywords": [
"CycloneDX",
Expand Down
10 changes: 6 additions & 4 deletions src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class BomBuilder {
this.console = console_
}

buildFromProjectDir (projectDir: string, process: NodeJS.Process): Models.Bom {
buildFromProjectDir (projectDir: null | string, process: NodeJS.Process): Models.Bom {
return this.buildFromNpmLs(
this.fetchNpmLs(projectDir, process)
)
Expand Down Expand Up @@ -113,7 +113,7 @@ export class BomBuilder {
return npmVersion
}

private fetchNpmLs (projectDir: string, process_: NodeJS.Process): any {
private fetchNpmLs (projectDir: null | string, process_: NodeJS.Process): any {
const npmRunner = makeNpmRunner(process_, this.console)

const npmVersion = this.getNpmVersion(npmRunner, process_)
Expand All @@ -130,7 +130,9 @@ export class BomBuilder {
: '--depth=255'
]

if (this.packageLockOnly) {
if (projectDir === null) {
args.push('--global')
} else if (this.packageLockOnly) {
if (npmVersion[0] >= 7) {
args.push('--package-lock-only')
} else {
Expand Down Expand Up @@ -165,7 +167,7 @@ export class BomBuilder {
let npmLsReturns: Buffer
try {
npmLsReturns = npmRunner(args, {
cwd: projectDir,
cwd: projectDir ?? undefined,
env: process_.env,
encoding: 'buffer',
maxBuffer: Number.MAX_SAFE_INTEGER // DIRTY but effective
Expand Down
61 changes: 40 additions & 21 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
*/

import { Builders, Enums, Factories, Serialize, Spec } from '@cyclonedx/cyclonedx-library'
import { Argument, Command, Option } from 'commander'
import { type AddHelpTextContext, Argument, Command, Option } from 'commander'
import { existsSync, openSync, writeSync } from 'fs'
import { dirname, resolve } from 'path'

Expand All @@ -40,6 +40,7 @@ const OutputStdOut = '-'
interface CommandOptions {
ignoreNpmErrors: boolean
packageLockOnly: boolean
global: boolean
omit: Omittable[]
specVersion: Spec.Version
flattenComponents: boolean
Expand All @@ -57,6 +58,12 @@ function makeCommand (process: NodeJS.Process): Command {
).usage(
// Need to add the `[--]` manually, to indicate how to stop a variadic option.
'[options] [--] [<package-manifest>]'
).addHelpText(
'after',
(context: AddHelpTextContext): string =>
'\nExample call:\n' +
` $ ${context.command.name()} --omit ${Omittable.Dev} --omit ${Omittable.Peer} -- ./my-project/package.json\n` +
` $ ${context.command.name()} --global\n`
).addOption(
new Option(
'--ignore-npm-errors',
Expand All @@ -69,6 +76,12 @@ function makeCommand (process: NodeJS.Process): Command {
'Whether to only use the lock file, ignoring "node_modules".\n' +
'This means the output will be based only on the few details in and the tree described by the "npm-shrinkwrap.json" or "package-lock.json", rather than the contents of "node_modules" directory.'
).default(false)
).addOption(
new Option(
'--global',
'Operates in "global" mode.\n' +
'[TODO: add more description]'
).default(false)
).addOption(
new Option(
'--omit <type...>',
Expand Down Expand Up @@ -180,28 +193,34 @@ export function run (process: NodeJS.Process): void {
const options: CommandOptions = program.opts()
myConsole.debug('DEBUG | options: %j', options)

const packageFile = resolve(process.cwd(), program.args[0] ?? 'package.json')
if (!existsSync(packageFile)) {
throw new Error(`missing project's manifest file: ${packageFile}`)
}
myConsole.debug('DEBUG | packageFile: %s', packageFile)
const projectDir = dirname(packageFile)
myConsole.info('INFO | projectDir: %s', projectDir)

if (existsSync(resolve(projectDir, 'npm-shrinkwrap.json'))) {
myConsole.debug('DEBUG | detected a npm shrinkwrap file')
} else if (existsSync(resolve(projectDir, 'package-lock.json'))) {
myConsole.debug('DEBUG | detected a package lock file')
} else if (!options.packageLockOnly && existsSync(resolve(projectDir, 'node_modules'))) {
myConsole.debug('DEBUG | detected a node_modules dir')
// npm7 and later also might put a `node_modules/.package-lock.json` file
let projectDir: null | string
if (options.global) {
projectDir = null
myConsole.info('INFO | operate in "global" mode')
} else {
myConsole.log('LOG | No evidence: no package lock file nor npm shrinkwrap file')
if (!options.packageLockOnly) {
myConsole.log('LOG | No evidence: no node_modules dir')
const _packageFile = resolve(process.cwd(), program.args[0] ?? 'package.json')
if (!existsSync(_packageFile)) {
throw new Error(`missing project's manifest file: ${_packageFile}`)
}
myConsole.debug('DEBUG | packageFile: %s', _packageFile)
projectDir = dirname(_packageFile)
myConsole.info('INFO | projectDir: %s', projectDir)

if (existsSync(resolve(projectDir, 'npm-shrinkwrap.json'))) {
myConsole.debug('DEBUG | detected a npm shrinkwrap file')
} else if (existsSync(resolve(projectDir, 'package-lock.json'))) {
myConsole.debug('DEBUG | detected a package lock file')
} else if (!options.packageLockOnly && existsSync(resolve(projectDir, 'node_modules'))) {
myConsole.debug('DEBUG | detected a node_modules dir')
// npm7 and later also might put a `node_modules/.package-lock.json` file
} else {
myConsole.log('LOG | No evidence: no package lock file nor npm shrinkwrap file')
if (!options.packageLockOnly) {
myConsole.log('LOG | No evidence: no node_modules dir')
}
myConsole.info('INFO | ? Did you forget to run `npm install` on your project accordingly ?')
throw new Error('missing evidence')
}
myConsole.info('INFO | ? Did you forget to run `npm install` on your project accordingly ?')
throw new Error('missing evidence')
}

const extRefFactory = new Factories.FromNodePackageJson.ExternalReferenceFactory()
Expand Down