Skip to content
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

feat: distribution url to SBOM #110

Merged
merged 34 commits into from
May 27, 2024
Merged
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
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,19 @@
],
"dependencies": {
"@cyclonedx/cyclonedx-library": "^6.8.0",
"@yarnpkg/cli": "^4.1.0",
"@yarnpkg/core": "^4.0.3",
"@yarnpkg/fslib": "^3.0.2",
"@yarnpkg/cli": "^4",
"@yarnpkg/core": "^4",
"@yarnpkg/fslib": "^3",
"@yarnpkg/plugin-git": "^3",
"clipanion": "^4.0.0-rc.3",
"hosted-git-info": "^7",
"normalize-package-data": "^3||^4||^5||^6",
"packageurl-js": "^1.2.1",
"typanion": "^3.14.0",
"xmlbuilder2": "^3.1.1"
},
"devDependencies": {
"@types/hosted-git-info": "^3.0.5",
"@types/mocha": "^10.0.6",
"@types/node": "ts5.4",
"@types/normalize-package-data": "^2.4.4",
Expand Down
20 changes: 20 additions & 0 deletions src/_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
*/

import { writeSync } from 'fs'
import GitHost from 'hosted-git-info'

export async function writeAllSync (fd: number, data: string): Promise<number> {
const b = Buffer.from(data)
Expand All @@ -39,3 +40,22 @@ export async function writeAllSync (fd: number, data: string): Promise<number> {
export function isString (v: any): v is string {
return typeof v === 'string'
}

export function tryRemoveSecretsFromUrl (url: string): string {
try {
const u = new URL(url)
u.password = ''
return u.toString()
} catch {
return url
}
}

export function tryRemoveSecretsFromGitUrl (gitUrl: string): string {
const gitInfo = GitHost.fromUrl(gitUrl)
if (gitInfo === undefined) {
return gitUrl
}
gitInfo.auth = undefined
return gitInfo.toString()
}
60 changes: 57 additions & 3 deletions src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@ Copyright (c) OWASP Foundation. All Rights Reserved.

// import submodules so to prevent load of unused not-tree-shakable dependencies - like 'AJV'
import type { FromNodePackageJson as PJB } from '@cyclonedx/cyclonedx-library/Builders'
import { ComponentType, LicenseAcknowledgement } from '@cyclonedx/cyclonedx-library/Enums'
import { ComponentType, ExternalReferenceType, LicenseAcknowledgement } from '@cyclonedx/cyclonedx-library/Enums'
import type { FromNodePackageJson as PJF } from '@cyclonedx/cyclonedx-library/Factories'
import { Bom, Component, type License, Property, type Tool } from '@cyclonedx/cyclonedx-library/Models'
import { Bom, Component, ExternalReference, type License, Property, type Tool } from '@cyclonedx/cyclonedx-library/Models'
import { BomUtility } from '@cyclonedx/cyclonedx-library/Utils'
import { Cache, type FetchOptions, type Locator, type LocatorHash, type Package, type Project, structUtils, ThrowReport, type Workspace } from '@yarnpkg/core'
import { ppath } from '@yarnpkg/fslib'
import { gitUtils as YarnPluginGitUtils } from '@yarnpkg/plugin-git'
import normalizePackageData from 'normalize-package-data'
import type { PackageURL } from 'packageurl-js'

import { getBuildtimeInfo } from './_buildtimeInfo'
import { isString } from './_helpers'
import { isString, tryRemoveSecretsFromGitUrl, tryRemoveSecretsFromUrl } from './_helpers'
import { PropertyNames, PropertyValueBool } from './properties'

type ManifestFetcher = (pkg: Package) => Promise<any>
Expand Down Expand Up @@ -193,6 +194,59 @@ export class BomBuilder {
return undefined
}

switch (true) {
case locator.reference.startsWith('workspace:'): {
// @TODO: add CDX-Property for it - cdx:yarn:reference:workspace = $workspaceName
// -- reminder: skip `workspace:.`
break
}
case locator.reference.startsWith('npm:'): {
// see https://github.com/yarnpkg/berry/blob/bfa6489467e0e11ee87268e01e38e4f7e8d4d4b0/packages/plugin-npm/sources/NpmHttpFetcher.ts#L51
const { params } = structUtils.parseRange(locator.reference)
if (params !== null && isString(params.__archiveUrl)) {
component.externalReferences.add(new ExternalReference(
tryRemoveSecretsFromUrl(params.__archiveUrl),
ExternalReferenceType.Distribution,
{ comment: 'as detected from YarnLocator property "reference::__archiveUrl"' }
))
}
// For range and remap there are no concrete evidence how the resolution was done on install-time.
// Therefore, do not do anything speculative.
break
}
case YarnPluginGitUtils.isGitUrl(locator.reference): {
component.externalReferences.add(new ExternalReference(
tryRemoveSecretsFromGitUrl(locator.reference),
ExternalReferenceType.VCS,
{ comment: 'as detected from YarnLocator property "reference"' }
))
break
}
case locator.reference.startsWith('http:') || locator.reference.startsWith('https:'): {
component.externalReferences.add(new ExternalReference(
tryRemoveSecretsFromUrl(locator.reference),
ExternalReferenceType.Distribution,
{ comment: 'as detected from YarnLocator property "reference"' }
))
break
}
case locator.reference.startsWith('link:'): {
// TODO: add CDX-Property for it - cdx:yarn:reference:link = relative path from workspace
// see https://github.com/yarnpkg/berry/tree/master/packages/plugin-link
break
}
case locator.reference.startsWith('portal:'): {
// TODO: add CDX-Property for it - cdx:yarn:reference:portal = relative path from workspace
// see https://github.com/yarnpkg/berry/tree/master/packages/plugin-link
break
}
case locator.reference.startsWith('file:'): {
// TODO: add CDX-Property for it - cdx:yarn:reference:portal = relative path from workspace
// see https://github.com/yarnpkg/berry/tree/master/packages/plugin-file
break
}
}

// even private packages may have a PURL for identification
component.purl = this.makePurl(component)

Expand Down

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

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

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

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

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

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

Loading
Loading