Skip to content
Open
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ $ yarn cyclonedx
━━━ Options ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
--lockfile-only Only use the yarn.lock file for dependency information.
No network calls will be made.
--production,--prod Exclude development dependencies.
(default: true if the NODE_ENV environment variable is set to "production", otherwise false)
--gather-license-texts Search for license files in components and include them as license evidence.
Expand Down
19 changes: 19 additions & 0 deletions src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ interface BomBuilderOptions {
reproducible?: BomBuilder['reproducible']
shortPURLs?: BomBuilder['shortPURLs']
gatherLicenseTexts?: BomBuilder['gatherLicenseTexts']
lockfileOnly?: BomBuilder['lockfileOnly']
}

export class BomBuilder {
Expand All @@ -65,6 +66,7 @@ export class BomBuilder {
readonly reproducible: boolean
readonly shortPURLs: boolean
readonly gatherLicenseTexts: boolean
readonly lockfileOnly: boolean

readonly console: Console

Expand All @@ -84,6 +86,7 @@ export class BomBuilder {
this.reproducible = options.reproducible ?? false
this.shortPURLs = options.shortPURLs ?? false
this.gatherLicenseTexts = options.gatherLicenseTexts ?? false
this.lockfileOnly = options.lockfileOnly ?? false

this.console = console_
}
Expand Down Expand Up @@ -157,6 +160,16 @@ export class BomBuilder {
}

private async makeManifestFetcher (project: Project): Promise<ManifestFetcher> {
if (this.lockfileOnly) {
/* eslint-disable-next-line @typescript-eslint/require-await -- needed for signature */
return async function (pkg: Package): Promise<NonNullable<any>> {
return {
name: pkg.name,
version: pkg.version
}
}
}

const fetcher = project.configuration.makeFetcher()
const fetcherOptions: FetchOptions = {
project,
Expand All @@ -180,6 +193,12 @@ export class BomBuilder {
}

private async makeLicenseEvidenceFetcher (project: Project): Promise<LicenseEvidenceFetcher> {
if (this.lockfileOnly) {
return async function * (_pkg: Package): AsyncGenerator<License> {
// no-op, yield nothing
}
}

const fetcher = project.configuration.makeFetcher()
const fetcherOptions: FetchOptions = {
project,
Expand Down
19 changes: 16 additions & 3 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import type { Types as SerializeTypes } from '@cyclonedx/cyclonedx-library/Seria
import { JSON as SerializeJSON, JsonSerializer, XML as SerializeXML, XmlSerializer } from '@cyclonedx/cyclonedx-library/Serialize'
import { SpecVersionDict, Version as SpecVersion } from '@cyclonedx/cyclonedx-library/Spec'
import type { CommandContext } from '@yarnpkg/core'
import { Configuration, Project, YarnVersion } from '@yarnpkg/core'
import { Configuration, Project, ThrowReport, YarnVersion } from '@yarnpkg/core'
import { npath, xfs } from '@yarnpkg/fslib'
import { Command, Option } from 'clipanion'
import spdxExpressionParse from "spdx-expression-parse"
Expand Down Expand Up @@ -76,6 +76,11 @@ export class MakeSbomCommand extends Command<CommandContext> {
details: 'Recursively scan workspace dependencies and emits them as Software-Bill-of-Materials(SBOM) in CycloneDX format.'
})

readonly lockfileOnly = Option.Boolean('--lockfile-only', false, {
description: 'Only use the yarn.lock file for dependency information.\n'+
'No network calls will be made.'
})

/* mimic option from yarn.
- see https://classic.yarnpkg.com/lang/en/docs/cli/install/#toc-yarn-install-production-true-false
- see https://yarnpkg.com/cli/workspaces/focus
Expand Down Expand Up @@ -158,6 +163,7 @@ export class MakeSbomCommand extends Command<CommandContext> {
outputReproducible: this.outputReproducible,
gatherLicenseTexts: this.gatherLicenseTexts,
verbosity: this.verbosity,
lockfileOnly: this.lockfileOnly,
projectDir
})

Expand All @@ -170,7 +176,13 @@ export class MakeSbomCommand extends Command<CommandContext> {
}
myConsole.debug('DEBUG | project:', project.cwd)
myConsole.debug('DEBUG | workspace:', workspace.cwd)
await workspace.project.restoreInstallState()

if (this.lockfileOnly) {
myConsole.info('INFO | skipping workspace installation state restoration (--lockfile-only)')
await workspace.project.resolveEverything({ lockfileOnly: true, report: new ThrowReport() })
} else {
await workspace.project.restoreInstallState()
}

const extRefFactory = new FromNodePackageJsonFactories.ExternalReferenceFactory()

Expand All @@ -187,7 +199,8 @@ export class MakeSbomCommand extends Command<CommandContext> {
metaComponentType: this.mcType,
reproducible: this.outputReproducible,
shortPURLs: this.shortPURLs,
gatherLicenseTexts: this.gatherLicenseTexts
gatherLicenseTexts: this.gatherLicenseTexts,
lockfileOnly: this.lockfileOnly
},
myConsole
)).buildFromWorkspace(workspace)
Expand Down
102 changes: 102 additions & 0 deletions tests/_data/snapshots/plain_lockfile-only.json.bin

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

76 changes: 76 additions & 0 deletions tests/_data/snapshots/plain_lockfile-only.xml.bin

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

Loading