Skip to content

Commit

Permalink
Merge branch 'main' into feat/workspace-support
Browse files Browse the repository at this point in the history
Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
  • Loading branch information
jkowalleck authored Dec 24, 2024
2 parents a5ff5bc + 3d7f8e1 commit ae6e4d3
Show file tree
Hide file tree
Showing 735 changed files with 350,709 additions and 299,436 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ concurrency:
cancel-in-progress: true

env:
NODE_ACTIVE_LTS: '20' # https://nodejs.org/en/about/releases/
NODE_ACTIVE_LTS: '22' # https://nodejs.org/en/about/releases/
DIST_DIR: dist
REPORTS_DIR: "CI_reports"
TESTS_REPORTS_ARTIFACT: tests-reports
Expand All @@ -43,7 +43,7 @@ jobs:
- name: setup subject
run: npm i --ignore-scripts --loglevel=silly
- name: build
run: npm run build
run: npm run build-dev
- name: artifact build result
# see https://github.com/actions/upload-artifact
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -175,6 +175,9 @@ jobs:
matrix:
include:
- npm-version: 'latest'
- npm-version: '^11'
## "node": "^20.17.0 || >=22.9.0"
node-version: '^22.9'
- npm-version: '^10'
## "node": "^18.17.0 || >=20.5.0"
node-version: '^20.5'
Expand Down
51 changes: 31 additions & 20 deletions .github/workflows/npm-ls_demo-results.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,24 @@ jobs:
matrix:
subject:
# - deps-from-git
# - alternative-package-registry
# - bundled-dependencies
# - dev-dependencies
# - juice-shop
# - local-dependencies
- alternative-package-registry
- bundled-dependencies
- dev-dependencies
- juice-shop
- local-dependencies
- local-workspaces
# - package-integrity
# - package-with-build-id
- package-integrity
- package-with-build-id
additional_npm-ls_args: [ '' ]
npm-version:
## see https://www.npmjs.com/package/npm?activeTab=versions
## see also: https://github.com/npm/cli/releases
- '10' # Current
- '9' # Legacy
- '8' # Legacy
- '7' # Legacy
- '6' # Legacy
- '11' # Current
#- '10' # Legacy
#- '9' # Legacy
#- '8' # Legacy
#- '7' # Legacy
#- '6' # Legacy
node-version:
## action based on https://github.com/actions/node-versions/releases
## see also: https://nodejs.org/en/about/releases/
Expand All @@ -51,24 +52,33 @@ jobs:
- macos-latest
include:
- subject: local-workspaces
additional_npm-ls_args: '--workspace==my-local-e'
npm-version: '10' # Current
node-version: '22' # Current
additional_npm-ls_args: '--workspace=my-local-e'
npm-version: '11' # Current
node-version: '22' # LTS
os: ubuntu-latest
- subject: local-workspaces
additional_npm-ls_args: '--workspace==my-local --workspace==my-local-e'
npm-version: '10' # Current
node-version: '22' # Current
additional_npm-ls_args: '--workspace=my-local --workspace=my-local-e'
npm-version: '11' # Current
node-version: '22' # LTS
os: ubuntu-latest
- subject: local-workspaces
additional_npm-ls_args: '--workspaces'
npm-version: '10' # Current
node-version: '22' # Current
npm-version: '11' # Current
node-version: '22' # LTS
os: ubuntu-latest
exclude:
- # macos-latest no longer supports node14
os: macos-latest
node-version: '14'
- # npm11 requires node ^20.17.0 || >=22.9.0
npm-version: '11'
node-version: '18'
- # npm11 requires node ^20.17.0 || >=22.9.0
npm-version: '11'
node-version: '16'
- # npm11 requires node ^20.17.0 || >=22.9.0
npm-version: '11'
node-version: '14'
- # npm10 requires node ^18.17.0 || >=20.5.0
npm-version: '10'
node-version: '16'
Expand Down Expand Up @@ -126,6 +136,7 @@ jobs:
working-directory: '${{ env.DEMO_ROOT_DIR}}/${{ matrix.subject }}'
- name: Artifact RESULTS
# see https://github.com/actions/upload-artifact
if: ${{ ! cancelled() }}
uses: actions/upload-artifact@v4
with:
name: '${{ env.RESULTS_ARTIFACT }}_${{ matrix.subject }}${{ matrix.additional_npm-ls_args }}_npm${{ matrix.npm-version }}_node${{ matrix.node-version }}_${{ matrix.os }}'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ env:
REPORTS_DIR: CI_reports
PACKED_DIR: CI_packed
PACKED_ARTIFACT: packed
NODE_ACTIVE_LTS: "20" # https://nodejs.org/en/about/releases/
NODE_ACTIVE_LTS: "22" # https://nodejs.org/en/about/releases/

jobs:
bump:
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ npm ci
## Build from source

```shell
npm run build
npm run build-dev
```

## Testing
Expand Down
11 changes: 10 additions & 1 deletion HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@ All notable changes to this project will be documented in this file.

<!-- unreleased changes go here -->

* Added
* Official support for `npm@11` ([#1245] via [#1249])
* Capability to gather license text evidences ([#256] via [#1243])
This feature can be controlled via CLI switch `--gather-license-texts`.
This feature is experimental. This feature is disabled per default.
* Dependencies
* No longer depend on `packageurlk-js` (via [#1237])
* No longer directly depend on `packageurl-js` (via [#1237])
* Build
* Use _TypeScript_ `v5.6.2` now, was `v5.5.3` (via [#1209], [#1218])

[#256]: https://github.com/CycloneDX/cyclonedx-node-npm/issues/256
[#1209]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1209
[#1218]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1218
[#1237]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1237
[#1243]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1243
[#1245]: https://github.com/CycloneDX/cyclonedx-node-npm/issues/1245
[#1249]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1249

## 1.19.3 -- 2024-07-15

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ and might have properties following [`cdx:npm` Namespace Taxonomy](https://githu
## Requirements

* `node` >= `14`
* `npm` in range `6 - 10`
* `npm` in range `6 - 11`

## Installation

Expand Down Expand Up @@ -87,6 +87,8 @@ Options:
--omit <type...> Dependency types to omit from the installation tree.
(can be set multiple times)
(choices: "dev", "optional", "peer", default: "dev" if the NODE_ENV environment variable is set to "production", otherwise empty)
--gather-license-texts Search for license files in components and include them as license evidence.
This feature is experimental. (default: false)
--flatten-components Whether to flatten the components.
This means the actual nesting of node packages is not represented in the SBOM result.
(default: false)
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"type": "commonjs",
"engines": {
"node": ">=14",
"npm": "6 - 10"
"npm": "6 - 11"
},
"directories": {
"doc": "docs",
Expand All @@ -91,6 +91,7 @@
"lint": "tsc --noEmit",
"prebuild": "node -r fs -e 'fs.rmSync(`dist`,{recursive:true,force:true})'",
"build": "tsc -b ./tsconfig.json",
"build-dev": "npm run -- build --sourceMap",
"cs-fix": "eslint --fix .",
"setup-tests": "node tests/integration/setup.js",
"test": "run-p --aggregate-output -lc test:*",
Expand Down
44 changes: 44 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 { readFileSync, writeSync } from 'fs'
import { extname, parse } from 'path'

export function loadJsonFile (path: string): any {
return JSON.parse(readFileSync(path, 'utf8'))
Expand Down Expand Up @@ -56,3 +57,46 @@ export function tryRemoveSecretsFromUrl (url: string): string {
return url
}
}

// region MIME

export type MimeType = string

const MIME_TEXT_PLAIN: MimeType = 'text/plain'

const MAP_TEXT_EXTENSION_MIME: Readonly<Record<string, MimeType>> = {
'': MIME_TEXT_PLAIN,
// https://www.iana.org/assignments/media-types/media-types.xhtml
'.csv': 'text/csv',
'.htm': 'text/html',
'.html': 'text/html',
'.md': 'text/markdown',
'.txt': MIME_TEXT_PLAIN,
'.rst': 'text/prs.fallenstein.rst',
'.xml': 'text/xml', // not `application/xml` -- our scope is text!
// add more mime types above this line. pull-requests welcome!
// license-specific files
'.license': MIME_TEXT_PLAIN,
'.licence': MIME_TEXT_PLAIN
} as const

export function getMimeForTextFile (filename: string): MimeType | undefined {
return MAP_TEXT_EXTENSION_MIME[extname(filename).toLowerCase()]
}

const LICENSE_FILENAME_BASE = new Set(['licence', 'license'])
const LICENSE_FILENAME_EXT = new Set([
'.apache',
'.bsd',
'.gpl',
'.mit'
])

export function getMimeForLicenseFile (filename: string): MimeType | undefined {
const { name, ext } = parse(filename.toLowerCase())
return LICENSE_FILENAME_BASE.has(name) && LICENSE_FILENAME_EXT.has(ext)
? MIME_TEXT_PLAIN
: MAP_TEXT_EXTENSION_MIME[ext]
}

// endregion MIME
54 changes: 52 additions & 2 deletions src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
*/

import { type Builders, Enums, type Factories, Models, Utils } from '@cyclonedx/cyclonedx-library'
import { existsSync } from 'fs'
import { existsSync, readdirSync, readFileSync } from 'fs'
import * as normalizePackageData from 'normalize-package-data'
import * as path from 'path'
import { join } from 'path'

import { isString, loadJsonFile, tryRemoveSecretsFromUrl } from './_helpers'
import { getMimeForLicenseFile, isString, loadJsonFile, tryRemoveSecretsFromUrl } from './_helpers'
import { makeNpmRunner, type runFunc } from './npmRunner'
import { PropertyNames, PropertyValueBool } from './properties'
import { versionCompare } from './versionCompare'
Expand All @@ -40,6 +41,7 @@ interface BomBuilderOptions {
workspace?: BomBuilder['workspace']
includeWorkspaceRoot?: BomBuilder['includeWorkspaceRoot']
workspaces?: BomBuilder['workspaces']
gatherLicenseTexts?: BomBuilder['gatherLicenseTexts']
}

type cPath = string
Expand All @@ -62,6 +64,7 @@ export class BomBuilder {
workspace: string[]
includeWorkspaceRoot: boolean
workspaces: boolean
gatherLicenseTexts: boolean

console: Console

Expand All @@ -88,6 +91,7 @@ export class BomBuilder {
this.workspace = options.workspace ?? []
this.includeWorkspaceRoot = options.includeWorkspaceRoot ?? false
this.workspaces = options.workspaces ?? true
this.gatherLicenseTexts = options.gatherLicenseTexts ?? false

this.console = console_
}
Expand Down Expand Up @@ -500,6 +504,23 @@ export class BomBuilder {
l.acknowledgement = Enums.LicenseAcknowledgement.Declared
})

if (this.gatherLicenseTexts) {
if (this.packageLockOnly) {
this.console.warn('WARN | Adding license text is ignored (package-lock-only is configured!) for %j', data.name)
} else {
component.evidence = new Models.ComponentEvidence()
for (const license of this.fetchLicenseEvidence(data?.path as string)) {
if (license != null) {
// only create a evidence if a license attachment is found
if (component.evidence == null) {
component.evidence = new Models.ComponentEvidence()
}
component.evidence.licenses.add(license)
}
}
}
}

if (isOptional || isDevOptional) {
component.scope = Enums.ComponentScope.Optional
}
Expand Down Expand Up @@ -645,6 +666,35 @@ export class BomBuilder {
}
}
}

readonly #LICENSE_FILENAME_PATTERN = /^(?:UN)?LICEN[CS]E|.\.LICEN[CS]E$|^NOTICE$/i

private * fetchLicenseEvidence (path: string): Generator<Models.License | null, void, void> {
const files = readdirSync(path)
for (const file of files) {
if (!this.#LICENSE_FILENAME_PATTERN.test(file)) {
continue
}

const contentType = getMimeForLicenseFile(file)
if (contentType === undefined) {
continue
}

const fp = join(path, file)
yield new Models.NamedLicense(
`file: ${file}`,
{
text: new Models.Attachment(
readFileSync(fp).toString('base64'),
{
contentType,
encoding: Enums.AttachmentEncoding.Base64
}
)
})
}
}
}

class DummyComponent extends Models.Component {
Expand Down
10 changes: 9 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ interface CommandOptions {
ignoreNpmErrors: boolean
packageLockOnly: boolean
omit: Omittable[]
specVersion: Spec.Version
gatherLicenseTexts: boolean
flattenComponents: boolean
shortPURLs: boolean
outputReproducible: boolean
specVersion: Spec.Version
outputFormat: OutputFormat
outputFile: string
validate: boolean | undefined
Expand Down Expand Up @@ -89,6 +90,12 @@ function makeCommand (process: NodeJS.Process): Command {
: [],
`"${Omittable.Dev}" if the NODE_ENV environment variable is set to "production", otherwise empty`
)
).addOption(
new Option(
'--gather-license-texts',
'Search for license files in components and include them as license evidence.\n' +
'This feature is experimental.'
).default(false)
).addOption(
new Option(
'--flatten-components',
Expand Down Expand Up @@ -280,6 +287,7 @@ export async function run (process: NodeJS.Process): Promise<number> {
metaComponentType: options.mcType,
packageLockOnly: options.packageLockOnly,
omitDependencyTypes: options.omit,
gatherLicenseTexts: options.gatherLicenseTexts,
reproducible: options.outputReproducible,
flattenComponents: options.flattenComponents,
shortPURLs: options.shortPURLs,
Expand Down
3 changes: 3 additions & 0 deletions tests/_data/dummy_projects/with-prepared/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

* linguist-vendored -text
**/* linguist-vendored -text
13 changes: 13 additions & 0 deletions tests/_data/dummy_projects/with-prepared/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
*
!/.gitignore
!/.gitattributes

!/node_modules
!/node_modules/*
!/node_modules/**/*
!/package.json
!/package-lock.json

!/setup.sh
!/README.md
!/LICENCE.mit
Loading

0 comments on commit ae6e4d3

Please sign in to comment.