Skip to content

Commit 6186d73

Browse files
committed
feat: builders.FromPath.LicenseEvidenceBuilder
Signed-off-by: Jan Kowalleck <[email protected]>
1 parent 414ce5e commit 6186d73

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed

src/_helpers/mime.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*!
2+
This file is part of CycloneDX JavaScript Library.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache-2.0
17+
Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
20+
21+
import {extname} from "path";
22+
23+
export type MimeType = string
24+
25+
const MAP_TEXT_EXTENSION_MIME: Readonly<Record<string, MimeType>> = {
26+
'': 'text/plain',
27+
'.licence': 'text/plain',
28+
'.license': 'text/plain',
29+
'.md': 'text/markdown',
30+
'.rst': 'text/prs.fallenstein.rst',
31+
'.txt': 'text/plain',
32+
'.xml': 'text/xml' // not `application/xml` -- our scope is text!
33+
} as const
34+
35+
export function getMimeForTextFile (filename: string): MimeType | undefined {
36+
return MAP_TEXT_EXTENSION_MIME[extname(filename).toLowerCase()]
37+
}

src/builders/fromPath.node.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*!
2+
This file is part of CycloneDX JavaScript Library.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
16+
SPDX-License-Identifier: Apache-2.0
17+
Copyright (c) OWASP Foundation. All Rights Reserved.
18+
*/
19+
20+
/**
21+
* Node-specifics.
22+
*/
23+
24+
import {readdirSync, readFileSync} from "fs";
25+
import {join, relative, resolve} from "path";
26+
27+
import {getMimeForTextFile} from "../_helpers/mime";
28+
import {AttachmentEncoding} from "../enums/attachmentEncoding";
29+
import {Attachment} from "../models/attachment";
30+
import {NamedLicense} from "../models/license";
31+
32+
33+
34+
/**
35+
* Node-specific LicenseEvidenceBuilder.
36+
*/
37+
export class LicenseEvidenceBuilder {
38+
39+
readonly #LICENSE_FILENAME_PATTERN = /^(?:UN)?LICEN[CS]E|.\.LICEN[CS]E$|^NOTICE$/i
40+
41+
/**
42+
* Return a license on success, returns undefined if it appears to bes no known text file.
43+
* Throws errors, if license attachment failed to create.
44+
*
45+
* @param file - path to file
46+
* @param relativeFrom - path the file shall be relative to
47+
* @returns {@link NamedLicense} on success
48+
*/
49+
public fromFile(file: string, relativeFrom: string | undefined = undefined): NamedLicense | undefined {
50+
const contentType = getMimeForTextFile(file)
51+
if (contentType === undefined) {
52+
return undefined
53+
}
54+
let lname
55+
if ( relativeFrom === undefined) {
56+
lname = `file: ${file}`
57+
} else {
58+
// `file` could be absolute or relative path - lets resolve it anyway
59+
file = resolve(relativeFrom, file)
60+
lname = `file: ${relative(relativeFrom, file)}`
61+
}
62+
return new NamedLicense(
63+
`file: ${lname}`,
64+
{
65+
text: new Attachment(
66+
// may throw if `readFileSync()` fails
67+
readFileSync(file).toString('base64'),
68+
{
69+
contentType,
70+
encoding: AttachmentEncoding.Base64
71+
}
72+
)
73+
})
74+
}
75+
76+
/**
77+
* Returns a generator for license evidences.
78+
* Throws errors, if dir cannot be inspected.
79+
*
80+
* @param dir - path to inspect
81+
* @param relativeFrom - path the dir shall be relative to
82+
*/
83+
public * fromDir(dir: string, relativeFrom: string | undefined = undefined): Generator<NamedLicense> {
84+
if ( relativeFrom !== undefined) {
85+
// `dir` could be absolute or relative path - lets resolve it anyway
86+
dir = resolve(relativeFrom, dir)
87+
}
88+
// may throw if `readdirSync()` fails
89+
const dcis = readdirSync(dir, { withFileTypes: true })
90+
for (const dci of dcis) {
91+
if (
92+
!dci.isFile() ||
93+
!this.#LICENSE_FILENAME_PATTERN.test(dci.name.toLowerCase())
94+
) {
95+
continue
96+
}
97+
98+
let le
99+
try {
100+
le = this.fromFile( join(dir, dci.name), relativeFrom)
101+
} catch (e) {
102+
continue
103+
}
104+
if (le !== undefined) {
105+
yield le
106+
}
107+
}
108+
}
109+
110+
}

src/builders/index.node.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
1818
*/
1919

2020
export * as FromNodePackageJson from './fromNodePackageJson.node'
21+
export * as FromPath from './fromPath.node'

0 commit comments

Comments
 (0)