Skip to content

Commit ae6e4d3

Browse files
authored
Merge branch 'main' into feat/workspace-support
Signed-off-by: Jan Kowalleck <[email protected]>
2 parents a5ff5bc + 3d7f8e1 commit ae6e4d3

File tree

735 files changed

+350709
-299436
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

735 files changed

+350709
-299436
lines changed

.github/workflows/nodejs.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ concurrency:
1919
cancel-in-progress: true
2020

2121
env:
22-
NODE_ACTIVE_LTS: '20' # https://nodejs.org/en/about/releases/
22+
NODE_ACTIVE_LTS: '22' # https://nodejs.org/en/about/releases/
2323
DIST_DIR: dist
2424
REPORTS_DIR: "CI_reports"
2525
TESTS_REPORTS_ARTIFACT: tests-reports
@@ -43,7 +43,7 @@ jobs:
4343
- name: setup subject
4444
run: npm i --ignore-scripts --loglevel=silly
4545
- name: build
46-
run: npm run build
46+
run: npm run build-dev
4747
- name: artifact build result
4848
# see https://github.com/actions/upload-artifact
4949
uses: actions/upload-artifact@v4
@@ -175,6 +175,9 @@ jobs:
175175
matrix:
176176
include:
177177
- npm-version: 'latest'
178+
- npm-version: '^11'
179+
## "node": "^20.17.0 || >=22.9.0"
180+
node-version: '^22.9'
178181
- npm-version: '^10'
179182
## "node": "^18.17.0 || >=20.5.0"
180183
node-version: '^20.5'

.github/workflows/npm-ls_demo-results.yml

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,24 @@ jobs:
2020
matrix:
2121
subject:
2222
# - deps-from-git
23-
# - alternative-package-registry
24-
# - bundled-dependencies
25-
# - dev-dependencies
26-
# - juice-shop
27-
# - local-dependencies
23+
- alternative-package-registry
24+
- bundled-dependencies
25+
- dev-dependencies
26+
- juice-shop
27+
- local-dependencies
2828
- local-workspaces
29-
# - package-integrity
30-
# - package-with-build-id
29+
- package-integrity
30+
- package-with-build-id
3131
additional_npm-ls_args: [ '' ]
3232
npm-version:
3333
## see https://www.npmjs.com/package/npm?activeTab=versions
3434
## see also: https://github.com/npm/cli/releases
35-
- '10' # Current
36-
- '9' # Legacy
37-
- '8' # Legacy
38-
- '7' # Legacy
39-
- '6' # Legacy
35+
- '11' # Current
36+
#- '10' # Legacy
37+
#- '9' # Legacy
38+
#- '8' # Legacy
39+
#- '7' # Legacy
40+
#- '6' # Legacy
4041
node-version:
4142
## action based on https://github.com/actions/node-versions/releases
4243
## see also: https://nodejs.org/en/about/releases/
@@ -51,24 +52,33 @@ jobs:
5152
- macos-latest
5253
include:
5354
- subject: local-workspaces
54-
additional_npm-ls_args: '--workspace==my-local-e'
55-
npm-version: '10' # Current
56-
node-version: '22' # Current
55+
additional_npm-ls_args: '--workspace=my-local-e'
56+
npm-version: '11' # Current
57+
node-version: '22' # LTS
5758
os: ubuntu-latest
5859
- subject: local-workspaces
59-
additional_npm-ls_args: '--workspace==my-local --workspace==my-local-e'
60-
npm-version: '10' # Current
61-
node-version: '22' # Current
60+
additional_npm-ls_args: '--workspace=my-local --workspace=my-local-e'
61+
npm-version: '11' # Current
62+
node-version: '22' # LTS
6263
os: ubuntu-latest
6364
- subject: local-workspaces
6465
additional_npm-ls_args: '--workspaces'
65-
npm-version: '10' # Current
66-
node-version: '22' # Current
66+
npm-version: '11' # Current
67+
node-version: '22' # LTS
6768
os: ubuntu-latest
6869
exclude:
6970
- # macos-latest no longer supports node14
7071
os: macos-latest
7172
node-version: '14'
73+
- # npm11 requires node ^20.17.0 || >=22.9.0
74+
npm-version: '11'
75+
node-version: '18'
76+
- # npm11 requires node ^20.17.0 || >=22.9.0
77+
npm-version: '11'
78+
node-version: '16'
79+
- # npm11 requires node ^20.17.0 || >=22.9.0
80+
npm-version: '11'
81+
node-version: '14'
7282
- # npm10 requires node ^18.17.0 || >=20.5.0
7383
npm-version: '10'
7484
node-version: '16'
@@ -126,6 +136,7 @@ jobs:
126136
working-directory: '${{ env.DEMO_ROOT_DIR}}/${{ matrix.subject }}'
127137
- name: Artifact RESULTS
128138
# see https://github.com/actions/upload-artifact
139+
if: ${{ ! cancelled() }}
129140
uses: actions/upload-artifact@v4
130141
with:
131142
name: '${{ env.RESULTS_ARTIFACT }}_${{ matrix.subject }}${{ matrix.additional_npm-ls_args }}_npm${{ matrix.npm-version }}_node${{ matrix.node-version }}_${{ matrix.os }}'

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ env:
3434
REPORTS_DIR: CI_reports
3535
PACKED_DIR: CI_packed
3636
PACKED_ARTIFACT: packed
37-
NODE_ACTIVE_LTS: "20" # https://nodejs.org/en/about/releases/
37+
NODE_ACTIVE_LTS: "22" # https://nodejs.org/en/about/releases/
3838

3939
jobs:
4040
bump:

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ npm ci
1616
## Build from source
1717

1818
```shell
19-
npm run build
19+
npm run build-dev
2020
```
2121

2222
## Testing

HISTORY.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,23 @@ All notable changes to this project will be documented in this file.
66

77
<!-- unreleased changes go here -->
88

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

19+
[#256]: https://github.com/CycloneDX/cyclonedx-node-npm/issues/256
1420
[#1209]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1209
1521
[#1218]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1218
1622
[#1237]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1237
23+
[#1243]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1243
24+
[#1245]: https://github.com/CycloneDX/cyclonedx-node-npm/issues/1245
25+
[#1249]: https://github.com/CycloneDX/cyclonedx-node-npm/pull/1249
1726

1827
## 1.19.3 -- 2024-07-15
1928

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ and might have properties following [`cdx:npm` Namespace Taxonomy](https://githu
2424
## Requirements
2525

2626
* `node` >= `14`
27-
* `npm` in range `6 - 10`
27+
* `npm` in range `6 - 11`
2828

2929
## Installation
3030

@@ -87,6 +87,8 @@ Options:
8787
--omit <type...> Dependency types to omit from the installation tree.
8888
(can be set multiple times)
8989
(choices: "dev", "optional", "peer", default: "dev" if the NODE_ENV environment variable is set to "production", otherwise empty)
90+
--gather-license-texts Search for license files in components and include them as license evidence.
91+
This feature is experimental. (default: false)
9092
--flatten-components Whether to flatten the components.
9193
This means the actual nesting of node packages is not represented in the SBOM result.
9294
(default: false)

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
"type": "commonjs",
7272
"engines": {
7373
"node": ">=14",
74-
"npm": "6 - 10"
74+
"npm": "6 - 11"
7575
},
7676
"directories": {
7777
"doc": "docs",
@@ -91,6 +91,7 @@
9191
"lint": "tsc --noEmit",
9292
"prebuild": "node -r fs -e 'fs.rmSync(`dist`,{recursive:true,force:true})'",
9393
"build": "tsc -b ./tsconfig.json",
94+
"build-dev": "npm run -- build --sourceMap",
9495
"cs-fix": "eslint --fix .",
9596
"setup-tests": "node tests/integration/setup.js",
9697
"test": "run-p --aggregate-output -lc test:*",

src/_helpers.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
1818
*/
1919

2020
import { readFileSync, writeSync } from 'fs'
21+
import { extname, parse } from 'path'
2122

2223
export function loadJsonFile (path: string): any {
2324
return JSON.parse(readFileSync(path, 'utf8'))
@@ -56,3 +57,46 @@ export function tryRemoveSecretsFromUrl (url: string): string {
5657
return url
5758
}
5859
}
60+
61+
// region MIME
62+
63+
export type MimeType = string
64+
65+
const MIME_TEXT_PLAIN: MimeType = 'text/plain'
66+
67+
const MAP_TEXT_EXTENSION_MIME: Readonly<Record<string, MimeType>> = {
68+
'': MIME_TEXT_PLAIN,
69+
// https://www.iana.org/assignments/media-types/media-types.xhtml
70+
'.csv': 'text/csv',
71+
'.htm': 'text/html',
72+
'.html': 'text/html',
73+
'.md': 'text/markdown',
74+
'.txt': MIME_TEXT_PLAIN,
75+
'.rst': 'text/prs.fallenstein.rst',
76+
'.xml': 'text/xml', // not `application/xml` -- our scope is text!
77+
// add more mime types above this line. pull-requests welcome!
78+
// license-specific files
79+
'.license': MIME_TEXT_PLAIN,
80+
'.licence': MIME_TEXT_PLAIN
81+
} as const
82+
83+
export function getMimeForTextFile (filename: string): MimeType | undefined {
84+
return MAP_TEXT_EXTENSION_MIME[extname(filename).toLowerCase()]
85+
}
86+
87+
const LICENSE_FILENAME_BASE = new Set(['licence', 'license'])
88+
const LICENSE_FILENAME_EXT = new Set([
89+
'.apache',
90+
'.bsd',
91+
'.gpl',
92+
'.mit'
93+
])
94+
95+
export function getMimeForLicenseFile (filename: string): MimeType | undefined {
96+
const { name, ext } = parse(filename.toLowerCase())
97+
return LICENSE_FILENAME_BASE.has(name) && LICENSE_FILENAME_EXT.has(ext)
98+
? MIME_TEXT_PLAIN
99+
: MAP_TEXT_EXTENSION_MIME[ext]
100+
}
101+
102+
// endregion MIME

src/builders.ts

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
1818
*/
1919

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

25-
import { isString, loadJsonFile, tryRemoveSecretsFromUrl } from './_helpers'
26+
import { getMimeForLicenseFile, isString, loadJsonFile, tryRemoveSecretsFromUrl } from './_helpers'
2627
import { makeNpmRunner, type runFunc } from './npmRunner'
2728
import { PropertyNames, PropertyValueBool } from './properties'
2829
import { versionCompare } from './versionCompare'
@@ -40,6 +41,7 @@ interface BomBuilderOptions {
4041
workspace?: BomBuilder['workspace']
4142
includeWorkspaceRoot?: BomBuilder['includeWorkspaceRoot']
4243
workspaces?: BomBuilder['workspaces']
44+
gatherLicenseTexts?: BomBuilder['gatherLicenseTexts']
4345
}
4446

4547
type cPath = string
@@ -62,6 +64,7 @@ export class BomBuilder {
6264
workspace: string[]
6365
includeWorkspaceRoot: boolean
6466
workspaces: boolean
67+
gatherLicenseTexts: boolean
6568

6669
console: Console
6770

@@ -88,6 +91,7 @@ export class BomBuilder {
8891
this.workspace = options.workspace ?? []
8992
this.includeWorkspaceRoot = options.includeWorkspaceRoot ?? false
9093
this.workspaces = options.workspaces ?? true
94+
this.gatherLicenseTexts = options.gatherLicenseTexts ?? false
9195

9296
this.console = console_
9397
}
@@ -500,6 +504,23 @@ export class BomBuilder {
500504
l.acknowledgement = Enums.LicenseAcknowledgement.Declared
501505
})
502506

507+
if (this.gatherLicenseTexts) {
508+
if (this.packageLockOnly) {
509+
this.console.warn('WARN | Adding license text is ignored (package-lock-only is configured!) for %j', data.name)
510+
} else {
511+
component.evidence = new Models.ComponentEvidence()
512+
for (const license of this.fetchLicenseEvidence(data?.path as string)) {
513+
if (license != null) {
514+
// only create a evidence if a license attachment is found
515+
if (component.evidence == null) {
516+
component.evidence = new Models.ComponentEvidence()
517+
}
518+
component.evidence.licenses.add(license)
519+
}
520+
}
521+
}
522+
}
523+
503524
if (isOptional || isDevOptional) {
504525
component.scope = Enums.ComponentScope.Optional
505526
}
@@ -645,6 +666,35 @@ export class BomBuilder {
645666
}
646667
}
647668
}
669+
670+
readonly #LICENSE_FILENAME_PATTERN = /^(?:UN)?LICEN[CS]E|.\.LICEN[CS]E$|^NOTICE$/i
671+
672+
private * fetchLicenseEvidence (path: string): Generator<Models.License | null, void, void> {
673+
const files = readdirSync(path)
674+
for (const file of files) {
675+
if (!this.#LICENSE_FILENAME_PATTERN.test(file)) {
676+
continue
677+
}
678+
679+
const contentType = getMimeForLicenseFile(file)
680+
if (contentType === undefined) {
681+
continue
682+
}
683+
684+
const fp = join(path, file)
685+
yield new Models.NamedLicense(
686+
`file: ${file}`,
687+
{
688+
text: new Models.Attachment(
689+
readFileSync(fp).toString('base64'),
690+
{
691+
contentType,
692+
encoding: Enums.AttachmentEncoding.Base64
693+
}
694+
)
695+
})
696+
}
697+
}
648698
}
649699

650700
class DummyComponent extends Models.Component {

src/cli.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ interface CommandOptions {
4343
ignoreNpmErrors: boolean
4444
packageLockOnly: boolean
4545
omit: Omittable[]
46-
specVersion: Spec.Version
46+
gatherLicenseTexts: boolean
4747
flattenComponents: boolean
4848
shortPURLs: boolean
4949
outputReproducible: boolean
50+
specVersion: Spec.Version
5051
outputFormat: OutputFormat
5152
outputFile: string
5253
validate: boolean | undefined
@@ -89,6 +90,12 @@ function makeCommand (process: NodeJS.Process): Command {
8990
: [],
9091
`"${Omittable.Dev}" if the NODE_ENV environment variable is set to "production", otherwise empty`
9192
)
93+
).addOption(
94+
new Option(
95+
'--gather-license-texts',
96+
'Search for license files in components and include them as license evidence.\n' +
97+
'This feature is experimental.'
98+
).default(false)
9299
).addOption(
93100
new Option(
94101
'--flatten-components',
@@ -280,6 +287,7 @@ export async function run (process: NodeJS.Process): Promise<number> {
280287
metaComponentType: options.mcType,
281288
packageLockOnly: options.packageLockOnly,
282289
omitDependencyTypes: options.omit,
290+
gatherLicenseTexts: options.gatherLicenseTexts,
283291
reproducible: options.outputReproducible,
284292
flattenComponents: options.flattenComponents,
285293
shortPURLs: options.shortPURLs,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
* linguist-vendored -text
3+
**/* linguist-vendored -text
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
*
2+
!/.gitignore
3+
!/.gitattributes
4+
5+
!/node_modules
6+
!/node_modules/*
7+
!/node_modules/**/*
8+
!/package.json
9+
!/package-lock.json
10+
11+
!/setup.sh
12+
!/README.md
13+
!/LICENCE.mit

0 commit comments

Comments
 (0)