@nodesecure/scanner 3.6.0 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -97
- package/index.js +14 -3
- package/package.json +16 -16
- package/src/class/dependency.class.js +4 -0
- package/src/depWalker.js +58 -18
- package/src/tarball.js +2 -3
- package/src/utils/isGitDependency.js +1 -10
- package/src/utils/mergeDependencies.js +6 -2
- package/types/scanner.d.ts +9 -2
- package/types/tarball.d.ts +63 -63
package/README.md
CHANGED
|
@@ -1,97 +1,104 @@
|
|
|
1
|
-
# NodeSecure Scanner
|
|
2
|
-

|
|
3
|
-
[](https://github.com/NodeSecure/scanner/commit-activity)
|
|
4
|
-
[
|
|
3
|
+
[](https://github.com/NodeSecure/scanner/commit-activity)
|
|
4
|
+
[](https://github.com/NodeSecure/scanner/blob/master/LICENSE)
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
⚡️ Run a static analysis of your module's dependencies.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- [Node.js](https://nodejs.org/en/) version 16 or higher
|
|
12
|
+
|
|
13
|
+
## Getting Started
|
|
14
|
+
|
|
15
|
+
This package is available in the Node Package Repository and can be easily installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or [yarn](https://yarnpkg.com).
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
$ npm i @nodesecure/scanner
|
|
19
|
+
# or
|
|
20
|
+
$ yarn add @nodesecure/scanner
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage example
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import * as scanner from "@nodesecure/scanner";
|
|
27
|
+
import fs from "fs/promises";
|
|
28
|
+
|
|
29
|
+
// CONSTANTS
|
|
30
|
+
const kPackagesToAnalyze = ["mocha", "cacache", "is-wsl"];
|
|
31
|
+
|
|
32
|
+
const payloads = await Promise.all(
|
|
33
|
+
kPackagesToAnalyze.map((name) => scanner.from(name))
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const promises = [];
|
|
37
|
+
for (let i = 0; i < kPackagesToAnalyze.length; i++) {
|
|
38
|
+
const data = JSON.stringify(payloads[i], null, 2);
|
|
39
|
+
|
|
40
|
+
promises.push(fs.writeFile(`${kPackagesToAnalyze[i]}.json`, data));
|
|
41
|
+
}
|
|
42
|
+
await Promise.allSettled(promises);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API
|
|
46
|
+
|
|
47
|
+
See `types/api.d.ts` for a complete TypeScript definition.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
function cwd(location: string, options?: Scanner.Options): Promise<Scanner.Payload>;
|
|
51
|
+
function from(packageName: string, options?: Omit<Scanner.Options, "includeDevDeps">): Promise<Scanner.Payload>;
|
|
52
|
+
function verify(packageName?: string | null): Promise<Scanner.VerifyPayload>;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`Options` is described with the following TypeScript interface:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
interface Options {
|
|
59
|
+
readonly maxDepth?: number;
|
|
60
|
+
readonly registry?: string | URL;
|
|
61
|
+
readonly usePackageLock?: boolean;
|
|
62
|
+
readonly includeDevDeps?: boolean;
|
|
63
|
+
readonly vulnerabilityStrategy: Strategy.Kind;
|
|
64
|
+
readonly forceRootAnalysis?: boolean;
|
|
65
|
+
readonly fullLockMode?: boolean;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Contributors ✨
|
|
70
|
+
|
|
71
|
+
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
72
|
+
[](#contributors-)
|
|
73
|
+
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
74
|
+
|
|
75
|
+
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
76
|
+
|
|
77
|
+
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
78
|
+
<!-- prettier-ignore-start -->
|
|
79
|
+
<!-- markdownlint-disable -->
|
|
80
|
+
<table>
|
|
81
|
+
<tbody>
|
|
82
|
+
<tr>
|
|
83
|
+
<td align="center"><a href="https://www.linkedin.com/in/thomas-gentilhomme/"><img src="https://avatars.githubusercontent.com/u/4438263?v=4?s=100" width="100px;" alt="Gentilhomme"/><br /><sub><b>Gentilhomme</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=fraxken" title="Code">💻</a> <a href="https://github.com/NodeSecure/scanner/commits?author=fraxken" title="Documentation">📖</a> <a href="https://github.com/NodeSecure/scanner/pulls?q=is%3Apr+reviewed-by%3Afraxken" title="Reviewed Pull Requests">👀</a> <a href="#security-fraxken" title="Security">🛡️</a> <a href="https://github.com/NodeSecure/scanner/issues?q=author%3Afraxken" title="Bug reports">🐛</a></td>
|
|
84
|
+
<td align="center"><a href="http://tonygo.dev"><img src="https://avatars.githubusercontent.com/u/22824417?v=4?s=100" width="100px;" alt="Tony Gorez"/><br /><sub><b>Tony Gorez</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=tony-go" title="Code">💻</a> <a href="https://github.com/NodeSecure/scanner/commits?author=tony-go" title="Documentation">📖</a> <a href="https://github.com/NodeSecure/scanner/pulls?q=is%3Apr+reviewed-by%3Atony-go" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/NodeSecure/scanner/issues?q=author%3Atony-go" title="Bug reports">🐛</a></td>
|
|
85
|
+
<td align="center"><a href="https://mickaelcroquet.fr"><img src="https://avatars.githubusercontent.com/u/23740372?v=4?s=100" width="100px;" alt="Haze"/><br /><sub><b>Haze</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=CroquetMickael" title="Code">💻</a></td>
|
|
86
|
+
<td align="center"><a href="https://github.com/mbalabash"><img src="https://avatars.githubusercontent.com/u/16868922?v=4?s=100" width="100px;" alt="Maksim Balabash"/><br /><sub><b>Maksim Balabash</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=mbalabash" title="Code">💻</a></td>
|
|
87
|
+
<td align="center"><a href="https://dev.to/antoinecoulon"><img src="https://avatars.githubusercontent.com/u/43391199?v=4?s=100" width="100px;" alt="Antoine Coulon"/><br /><sub><b>Antoine Coulon</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=antoine-coulon" title="Code">💻</a> <a href="#security-antoine-coulon" title="Security">🛡️</a></td>
|
|
88
|
+
<td align="center"><a href="https://www.linkedin.com/in/nicolas-hallaert/"><img src="https://avatars.githubusercontent.com/u/39910164?v=4?s=100" width="100px;" alt="Nicolas Hallaert"/><br /><sub><b>Nicolas Hallaert</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=Rossb0b" title="Code">💻</a></td>
|
|
89
|
+
<td align="center"><a href="http://sofiand.github.io/portfolio-client/"><img src="https://avatars.githubusercontent.com/u/39944043?v=4?s=100" width="100px;" alt="Yefis"/><br /><sub><b>Yefis</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=SofianD" title="Code">💻</a></td>
|
|
90
|
+
</tr>
|
|
91
|
+
<tr>
|
|
92
|
+
<td align="center"><a href="https://www.linkedin.com/in/franck-hallaert/"><img src="https://avatars.githubusercontent.com/u/110826655?v=4?s=100" width="100px;" alt="Franck Hallaert"/><br /><sub><b>Franck Hallaert</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=Aekk0" title="Code">💻</a></td>
|
|
93
|
+
<td align="center"><a href="https://www.linkedin.com/in/ange-tekeu-a155811b4/"><img src="https://avatars.githubusercontent.com/u/35274201?v=4?s=100" width="100px;" alt="Ange TEKEU"/><br /><sub><b>Ange TEKEU</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=tekeuange23" title="Code">💻</a></td>
|
|
94
|
+
</tr>
|
|
95
|
+
</tbody>
|
|
96
|
+
</table>
|
|
97
|
+
|
|
98
|
+
<!-- markdownlint-restore -->
|
|
99
|
+
<!-- prettier-ignore-end -->
|
|
100
|
+
|
|
101
|
+
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
MIT
|
package/index.js
CHANGED
|
@@ -19,7 +19,16 @@ import * as tarball from "./src/tarball.js";
|
|
|
19
19
|
const kDefaultCwdOptions = { forceRootAnalysis: true, usePackageLock: true, includeDevDeps: false };
|
|
20
20
|
|
|
21
21
|
export async function cwd(location = process.cwd(), options = {}, logger = new Logger()) {
|
|
22
|
-
const
|
|
22
|
+
const registry = options.registry ? new URL(options.registry).toString() : getLocalRegistryURL();
|
|
23
|
+
|
|
24
|
+
const finalizedOptions = Object.assign(
|
|
25
|
+
{ location },
|
|
26
|
+
kDefaultCwdOptions,
|
|
27
|
+
{
|
|
28
|
+
...options,
|
|
29
|
+
registry
|
|
30
|
+
}
|
|
31
|
+
);
|
|
23
32
|
|
|
24
33
|
logger.start(ScannerLoggerEvents.manifest.read);
|
|
25
34
|
const packagePath = path.join(location, "package.json");
|
|
@@ -30,13 +39,15 @@ export async function cwd(location = process.cwd(), options = {}, logger = new L
|
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
export async function from(packageName, options, logger = new Logger()) {
|
|
42
|
+
const registry = options.registry ? new URL(options.registry).toString() : getLocalRegistryURL();
|
|
43
|
+
|
|
33
44
|
logger.start(ScannerLoggerEvents.manifest.fetch);
|
|
34
45
|
const manifest = await pacote.manifest(packageName, {
|
|
35
|
-
...NPM_TOKEN, registry
|
|
46
|
+
...NPM_TOKEN, registry, cache: `${os.homedir()}/.npm`
|
|
36
47
|
});
|
|
37
48
|
logger.end(ScannerLoggerEvents.manifest.fetch);
|
|
38
49
|
|
|
39
|
-
return depWalker(manifest, options, logger);
|
|
50
|
+
return depWalker(manifest, Object.assign(options, { registry }), logger);
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
export async function verify(packageName = null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodesecure/scanner",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "A package API to run a static analysis of your module's dependencies.",
|
|
5
5
|
"exports": "./index.js",
|
|
6
6
|
"engines": {
|
|
@@ -48,39 +48,39 @@
|
|
|
48
48
|
},
|
|
49
49
|
"homepage": "https://github.com/NodeSecure/scanner#readme",
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@nodesecure/eslint-config": "^1.
|
|
51
|
+
"@nodesecure/eslint-config": "^1.5.0",
|
|
52
52
|
"@slimio/is": "^1.5.1",
|
|
53
53
|
"@small-tech/esm-tape-runner": "^2.0.0",
|
|
54
54
|
"@small-tech/tap-monkey": "^1.4.0",
|
|
55
|
-
"@types/node": "^
|
|
56
|
-
"c8": "^7.
|
|
55
|
+
"@types/node": "^18.11.9",
|
|
56
|
+
"c8": "^7.12.0",
|
|
57
57
|
"cross-env": "^7.0.3",
|
|
58
|
-
"dotenv": "^16.0.
|
|
59
|
-
"eslint": "^8.
|
|
60
|
-
"get-folder-size": "^
|
|
58
|
+
"dotenv": "^16.0.3",
|
|
59
|
+
"eslint": "^8.27.0",
|
|
60
|
+
"get-folder-size": "^4.0.0",
|
|
61
61
|
"pkg-ok": "^3.0.0",
|
|
62
|
-
"sinon": "^14.0.
|
|
62
|
+
"sinon": "^14.0.1",
|
|
63
63
|
"snap-shot-core": "^10.2.4",
|
|
64
|
-
"tape": "^5.
|
|
64
|
+
"tape": "^5.6.1"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@nodesecure/flags": "^2.
|
|
67
|
+
"@nodesecure/flags": "^2.4.0",
|
|
68
68
|
"@nodesecure/fs-walk": "^1.0.0",
|
|
69
|
-
"@nodesecure/i18n": "^
|
|
70
|
-
"@nodesecure/js-x-ray": "^
|
|
71
|
-
"@nodesecure/npm-registry-sdk": "^1.
|
|
69
|
+
"@nodesecure/i18n": "^2.0.0",
|
|
70
|
+
"@nodesecure/js-x-ray": "^5.1.0",
|
|
71
|
+
"@nodesecure/npm-registry-sdk": "^1.4.1",
|
|
72
72
|
"@nodesecure/ntlp": "^2.1.0",
|
|
73
73
|
"@nodesecure/utils": "^1.0.0",
|
|
74
74
|
"@nodesecure/vuln": "^1.7.0",
|
|
75
75
|
"@npm/types": "^1.0.2",
|
|
76
|
-
"@npmcli/arborist": "^
|
|
76
|
+
"@npmcli/arborist": "^6.1.1",
|
|
77
77
|
"@slimio/lock": "^1.0.0",
|
|
78
78
|
"builtins": "^5.0.1",
|
|
79
79
|
"combine-async-iterators": "^2.0.1",
|
|
80
80
|
"itertools": "^1.7.1",
|
|
81
81
|
"lodash.difference": "^4.5.0",
|
|
82
|
-
"pacote": "^
|
|
83
|
-
"semver": "^7.3.
|
|
82
|
+
"pacote": "^15.0.6",
|
|
83
|
+
"semver": "^7.3.8"
|
|
84
84
|
},
|
|
85
85
|
"type": "module"
|
|
86
86
|
}
|
|
@@ -9,6 +9,8 @@ export default class Dependency {
|
|
|
9
9
|
this.name = name;
|
|
10
10
|
this.version = version;
|
|
11
11
|
this.dev = false;
|
|
12
|
+
this.existOnRemoteRegistry = true;
|
|
13
|
+
this.alias = {};
|
|
12
14
|
|
|
13
15
|
if (parent !== null) {
|
|
14
16
|
parent.dependencyCount++;
|
|
@@ -62,6 +64,7 @@ export default class Dependency {
|
|
|
62
64
|
id: typeof customId === "number" ? customId : Dependency.currentId++,
|
|
63
65
|
usedBy: this.parent,
|
|
64
66
|
isDevDependency: this.dev,
|
|
67
|
+
existOnRemoteRegistry: this.existOnRemoteRegistry,
|
|
65
68
|
flags: this.flags,
|
|
66
69
|
description: "",
|
|
67
70
|
size: 0,
|
|
@@ -76,6 +79,7 @@ export default class Dependency {
|
|
|
76
79
|
minified: [],
|
|
77
80
|
unused: [],
|
|
78
81
|
missing: [],
|
|
82
|
+
alias: this.alias,
|
|
79
83
|
required_files: [],
|
|
80
84
|
required_nodejs: [],
|
|
81
85
|
required_thirdparty: [],
|
package/src/depWalker.js
CHANGED
|
@@ -11,7 +11,6 @@ import pacote from "pacote";
|
|
|
11
11
|
import Arborist from "@npmcli/arborist";
|
|
12
12
|
import Lock from "@slimio/lock";
|
|
13
13
|
import * as vuln from "@nodesecure/vuln";
|
|
14
|
-
import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk";
|
|
15
14
|
import { ScannerLoggerEvents } from "./constants.js";
|
|
16
15
|
|
|
17
16
|
// Import Internal Dependencies
|
|
@@ -31,24 +30,38 @@ const { version: packageVersion } = JSON.parse(
|
|
|
31
30
|
);
|
|
32
31
|
|
|
33
32
|
export async function* searchDeepDependencies(packageName, gitURL, options) {
|
|
34
|
-
const { exclude, currDepth = 0, parent, maxDepth } = options;
|
|
33
|
+
const { exclude, currDepth = 0, parent, maxDepth, registry } = options;
|
|
35
34
|
|
|
36
35
|
const { name, version, deprecated, ...pkg } = await pacote.manifest(gitURL ?? packageName, {
|
|
37
36
|
...NPM_TOKEN,
|
|
38
|
-
registry
|
|
37
|
+
registry,
|
|
39
38
|
cache: `${os.homedir()}/.npm`
|
|
40
39
|
});
|
|
41
|
-
const { dependencies, customResolvers } = mergeDependencies(pkg);
|
|
40
|
+
const { dependencies, customResolvers, alias } = mergeDependencies(pkg);
|
|
42
41
|
|
|
43
42
|
const current = new Dependency(name, version, parent);
|
|
44
|
-
|
|
43
|
+
current.alias = Object.fromEntries(alias);
|
|
44
|
+
|
|
45
|
+
if (gitURL !== null) {
|
|
46
|
+
current.isGit(gitURL);
|
|
47
|
+
try {
|
|
48
|
+
await pacote.manifest(`${name}@${version}`, {
|
|
49
|
+
...NPM_TOKEN,
|
|
50
|
+
registry,
|
|
51
|
+
cache: `${os.homedir()}/.npm`
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
current.existOnRemoteRegistry = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
45
58
|
current.addFlag("isDeprecated", deprecated === true);
|
|
46
59
|
current.addFlag("hasCustomResolver", customResolvers.size > 0);
|
|
47
60
|
current.addFlag("hasDependencies", dependencies.size > 0);
|
|
48
61
|
|
|
49
62
|
if (currDepth !== maxDepth) {
|
|
50
63
|
const config = {
|
|
51
|
-
exclude, currDepth: currDepth + 1, parent: current, maxDepth
|
|
64
|
+
exclude, currDepth: currDepth + 1, parent: current, maxDepth, registry
|
|
52
65
|
};
|
|
53
66
|
|
|
54
67
|
const gitDependencies = iter.filter(customResolvers.entries(), ([, valueStr]) => isGitDependency(valueStr));
|
|
@@ -76,7 +89,7 @@ export async function* searchDeepDependencies(packageName, gitURL, options) {
|
|
|
76
89
|
}
|
|
77
90
|
|
|
78
91
|
export async function* deepReadEdges(currentPackageName, options) {
|
|
79
|
-
const { to, parent, exclude, fullLockMode, includeDevDeps } = options;
|
|
92
|
+
const { to, parent, exclude, fullLockMode, includeDevDeps, registry } = options;
|
|
80
93
|
const { version, integrity = to.integrity } = to.package;
|
|
81
94
|
|
|
82
95
|
const updatedVersion = version === "*" || typeof version === "undefined" ? "latest" : version;
|
|
@@ -86,14 +99,19 @@ export async function* deepReadEdges(currentPackageName, options) {
|
|
|
86
99
|
if (fullLockMode && !includeDevDeps) {
|
|
87
100
|
const { deprecated, _integrity, ...pkg } = await pacote.manifest(`${currentPackageName}@${updatedVersion}`, {
|
|
88
101
|
...NPM_TOKEN,
|
|
89
|
-
registry
|
|
102
|
+
registry,
|
|
90
103
|
cache: `${os.homedir()}/.npm`
|
|
91
104
|
});
|
|
92
|
-
const { customResolvers } = mergeDependencies(pkg);
|
|
105
|
+
const { customResolvers, alias } = mergeDependencies(pkg);
|
|
93
106
|
|
|
107
|
+
current.alias = Object.fromEntries(alias);
|
|
94
108
|
current.addFlag("hasValidIntegrity", _integrity === integrity);
|
|
95
109
|
current.addFlag("isDeprecated");
|
|
96
110
|
current.addFlag("hasCustomResolver", customResolvers.size > 0);
|
|
111
|
+
|
|
112
|
+
if (isGitDependency(to.resolved)) {
|
|
113
|
+
current.isGit(to.resolved);
|
|
114
|
+
}
|
|
97
115
|
}
|
|
98
116
|
current.addFlag("hasDependencies", to.edgesOut.size > 0);
|
|
99
117
|
|
|
@@ -108,7 +126,7 @@ export async function* deepReadEdges(currentPackageName, options) {
|
|
|
108
126
|
}
|
|
109
127
|
else {
|
|
110
128
|
exclude.set(cleanName, new Set([current.fullName]));
|
|
111
|
-
yield* deepReadEdges(packageName, { parent: current, to: toNode, exclude });
|
|
129
|
+
yield* deepReadEdges(packageName, { parent: current, to: toNode, exclude, registry });
|
|
112
130
|
}
|
|
113
131
|
}
|
|
114
132
|
yield current;
|
|
@@ -118,11 +136,24 @@ export async function* getRootDependencies(manifest, options) {
|
|
|
118
136
|
const {
|
|
119
137
|
maxDepth = 4, exclude,
|
|
120
138
|
usePackageLock, fullLockMode, includeDevDeps,
|
|
121
|
-
location
|
|
139
|
+
location,
|
|
140
|
+
registry
|
|
122
141
|
} = options;
|
|
123
142
|
|
|
124
|
-
const { dependencies, customResolvers } = mergeDependencies(manifest, void 0);
|
|
143
|
+
const { dependencies, customResolvers, alias } = mergeDependencies(manifest, void 0);
|
|
125
144
|
const parent = new Dependency(manifest.name, manifest.version);
|
|
145
|
+
parent.alias = Object.fromEntries(alias);
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
await pacote.manifest(`${manifest.name}@${manifest.version}`, {
|
|
149
|
+
...NPM_TOKEN,
|
|
150
|
+
registry,
|
|
151
|
+
cache: `${os.homedir()}/.npm`
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
parent.existOnRemoteRegistry = false;
|
|
156
|
+
}
|
|
126
157
|
parent.addFlag("hasCustomResolver", customResolvers.size > 0);
|
|
127
158
|
parent.addFlag("hasDependencies", dependencies.size > 0);
|
|
128
159
|
|
|
@@ -131,7 +162,7 @@ export async function* getRootDependencies(manifest, options) {
|
|
|
131
162
|
const arb = new Arborist({
|
|
132
163
|
...NPM_TOKEN,
|
|
133
164
|
path: location,
|
|
134
|
-
registry
|
|
165
|
+
registry
|
|
135
166
|
});
|
|
136
167
|
let tree;
|
|
137
168
|
try {
|
|
@@ -146,11 +177,18 @@ export async function* getRootDependencies(manifest, options) {
|
|
|
146
177
|
...iter
|
|
147
178
|
.filter(tree.edgesOut.entries(), ([, { to }]) => to !== null && (includeDevDeps ? true : (!to.dev || to.isWorkspace)))
|
|
148
179
|
.map(([packageName, { to }]) => [packageName, to.isWorkspace ? to.target : to])
|
|
149
|
-
.map(([packageName, to]) => deepReadEdges(packageName, {
|
|
180
|
+
.map(([packageName, to]) => deepReadEdges(packageName, {
|
|
181
|
+
to,
|
|
182
|
+
parent,
|
|
183
|
+
fullLockMode,
|
|
184
|
+
includeDevDeps,
|
|
185
|
+
exclude,
|
|
186
|
+
registry
|
|
187
|
+
}))
|
|
150
188
|
];
|
|
151
189
|
}
|
|
152
190
|
else {
|
|
153
|
-
const configRef = { exclude, maxDepth, parent };
|
|
191
|
+
const configRef = { exclude, maxDepth, parent, registry };
|
|
154
192
|
iterators = [
|
|
155
193
|
...iter.filter(customResolvers.entries(), ([, valueStr]) => isGitDependency(valueStr))
|
|
156
194
|
.map(([depName, valueStr]) => searchDeepDependencies(depName, valueStr, configRef)),
|
|
@@ -189,7 +227,8 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
|
|
|
189
227
|
fullLockMode = false,
|
|
190
228
|
maxDepth,
|
|
191
229
|
location,
|
|
192
|
-
vulnerabilityStrategy = vuln.strategies.NONE
|
|
230
|
+
vulnerabilityStrategy = vuln.strategies.NONE,
|
|
231
|
+
registry
|
|
193
232
|
} = options;
|
|
194
233
|
|
|
195
234
|
// Create TMP directory
|
|
@@ -218,7 +257,7 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
|
|
|
218
257
|
const tarballLocker = new Lock({ maxConcurrent: 5 });
|
|
219
258
|
tarballLocker.on("freeOne", () => logger.tick(ScannerLoggerEvents.analysis.tarball));
|
|
220
259
|
|
|
221
|
-
const rootDepsOptions = { maxDepth, exclude, usePackageLock, fullLockMode, includeDevDeps, location };
|
|
260
|
+
const rootDepsOptions = { maxDepth, exclude, usePackageLock, fullLockMode, includeDevDeps, location, registry };
|
|
222
261
|
for await (const currentDep of getRootDependencies(manifest, rootDepsOptions)) {
|
|
223
262
|
const { name, version, dev } = currentDep;
|
|
224
263
|
const current = currentDep.exportAsPlainObject(name === manifest.name ? 0 : void 0);
|
|
@@ -267,7 +306,8 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
|
|
|
267
306
|
location,
|
|
268
307
|
tmpLocation: forceRootAnalysis && name === manifest.name ? null : tmpLocation,
|
|
269
308
|
locker: tarballLocker,
|
|
270
|
-
logger
|
|
309
|
+
logger,
|
|
310
|
+
registry
|
|
271
311
|
}));
|
|
272
312
|
}
|
|
273
313
|
}
|
package/src/tarball.js
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
NPM_TOKEN
|
|
15
15
|
} from "./utils/index.js";
|
|
16
16
|
import * as manifest from "./manifest.js";
|
|
17
|
-
import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk";
|
|
18
17
|
|
|
19
18
|
// CONSTANTS
|
|
20
19
|
const kNativeCodeExtensions = new Set([".gyp", ".c", ".cpp", ".node", ".so", ".h"]);
|
|
@@ -47,7 +46,7 @@ export async function scanJavascriptFile(dest, file, packageName) {
|
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
export async function scanDirOrArchive(name, version, options) {
|
|
50
|
-
const { ref, location = process.cwd(), tmpLocation, locker } = options;
|
|
49
|
+
const { ref, location = process.cwd(), tmpLocation, locker, registry } = options;
|
|
51
50
|
|
|
52
51
|
const isNpmTarball = !(tmpLocation === null);
|
|
53
52
|
const dest = isNpmTarball ? path.join(tmpLocation, `${name}@${version}`) : location;
|
|
@@ -58,7 +57,7 @@ export async function scanDirOrArchive(name, version, options) {
|
|
|
58
57
|
if (isNpmTarball) {
|
|
59
58
|
await pacote.extract(ref.flags.includes("isGit") ? ref.gitUrl : `${name}@${version}`, dest, {
|
|
60
59
|
...NPM_TOKEN,
|
|
61
|
-
registry
|
|
60
|
+
registry,
|
|
62
61
|
cache: `${os.homedir()}/.npm`
|
|
63
62
|
});
|
|
64
63
|
await timers.setImmediate();
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const kGitVersionVariants = ["git:", "git+", "github:"];
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* @example isGitDependency("github:NodeSecure/scanner") // => true
|
|
5
3
|
* @example isGitDependency("git+ssh://git@github.com:npm/cli#semver:^5.0") // => true
|
|
@@ -9,12 +7,5 @@ const kGitVersionVariants = ["git:", "git+", "github:"];
|
|
|
9
7
|
* @returns {boolean}
|
|
10
8
|
*/
|
|
11
9
|
export function isGitDependency(version) {
|
|
12
|
-
|
|
13
|
-
if (version.startsWith(variant)) {
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return false;
|
|
10
|
+
return /^git(:|\+|hub:)/.test(version);
|
|
19
11
|
}
|
|
20
|
-
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export function mergeDependencies(manifest, types = ["dependencies"]) {
|
|
2
2
|
const dependencies = new Map();
|
|
3
3
|
const customResolvers = new Map();
|
|
4
|
+
const alias = new Map();
|
|
4
5
|
|
|
5
6
|
for (const fieldName of types) {
|
|
6
7
|
if (!Reflect.has(manifest, fieldName)) {
|
|
@@ -15,12 +16,15 @@ export function mergeDependencies(manifest, types = ["dependencies"]) {
|
|
|
15
16
|
*/
|
|
16
17
|
if (/^([a-zA-Z]+:|git\+|\.\\)/.test(version)) {
|
|
17
18
|
customResolvers.set(name, version);
|
|
18
|
-
|
|
19
|
+
if (!version.startsWith("npm:")) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
alias.set(name, version.slice(4));
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
dependencies.set(name, version);
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
return { dependencies, customResolvers };
|
|
29
|
+
return { dependencies, customResolvers, alias };
|
|
26
30
|
}
|
package/types/scanner.d.ts
CHANGED
|
@@ -34,6 +34,11 @@ declare namespace Scanner {
|
|
|
34
34
|
/** Id of the package (useful for usedBy relation) */
|
|
35
35
|
id: number;
|
|
36
36
|
isDevDependency: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Tell if the given package exist on the configured remote registry (npm by default)
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
existOnRemoteRegistry: boolean;
|
|
37
42
|
/** By whom (id) is used the package */
|
|
38
43
|
usedBy: Record<string, string>;
|
|
39
44
|
/** Size on disk of the extracted tarball (in bytes) */
|
|
@@ -56,7 +61,7 @@ declare namespace Scanner {
|
|
|
56
61
|
*
|
|
57
62
|
* @see https://github.com/NodeSecure/js-x-ray/blob/master/WARNINGS.md
|
|
58
63
|
*/
|
|
59
|
-
warnings: JSXRay.Warning<JSXRay.
|
|
64
|
+
warnings: JSXRay.Warning<JSXRay.WarningDefault>[];
|
|
60
65
|
/** Tarball composition (files and dependencies) */
|
|
61
66
|
composition: {
|
|
62
67
|
/** Files extensions (.js, .md, .exe etc..) */
|
|
@@ -64,6 +69,7 @@ declare namespace Scanner {
|
|
|
64
69
|
files: string[];
|
|
65
70
|
/** Minified files (foo.min.js etc..) */
|
|
66
71
|
minified: string[];
|
|
72
|
+
alias: Record<string, string>;
|
|
67
73
|
required_files: string[];
|
|
68
74
|
required_thirdparty: string[];
|
|
69
75
|
required_nodejs: string[];
|
|
@@ -155,7 +161,7 @@ declare namespace Scanner {
|
|
|
155
161
|
licenses: License[];
|
|
156
162
|
ast: {
|
|
157
163
|
dependencies: Record<string, JSXRay.Dependency>;
|
|
158
|
-
warnings: JSXRay.Warning<JSXRay.
|
|
164
|
+
warnings: JSXRay.Warning<JSXRay.WarningDefault>[];
|
|
159
165
|
};
|
|
160
166
|
}
|
|
161
167
|
|
|
@@ -166,6 +172,7 @@ declare namespace Scanner {
|
|
|
166
172
|
* @default 4
|
|
167
173
|
*/
|
|
168
174
|
readonly maxDepth?: number;
|
|
175
|
+
readonly registry?: string | URL;
|
|
169
176
|
/**
|
|
170
177
|
* Use root package-lock.json. This will have the effect of triggering the Arborist package.
|
|
171
178
|
*
|
package/types/tarball.d.ts
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
import ntlp from "@nodesecure/ntlp";
|
|
2
|
-
import Locker from "@slimio/lock";
|
|
3
|
-
import Logger from "../src/logger.class";
|
|
4
|
-
|
|
5
|
-
export = tarball;
|
|
6
|
-
|
|
7
|
-
declare namespace tarball {
|
|
8
|
-
export interface ManifestData {
|
|
9
|
-
/** Dependencies in package.json */
|
|
10
|
-
packageDeps: string[];
|
|
11
|
-
/** DevDependencies in package.json */
|
|
12
|
-
packageDevDeps: string[];
|
|
13
|
-
/** Does package.json contain a 'gypfile' property ? */
|
|
14
|
-
packageGyp: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface ScannedFileResult {
|
|
18
|
-
/** Dependencies in try/catch block (probably optional dependencies) */
|
|
19
|
-
inTryDeps: string[];
|
|
20
|
-
/** Dependencies required or imported */
|
|
21
|
-
dependencies: string[];
|
|
22
|
-
/** Required or imported javascript files */
|
|
23
|
-
filesDependencies: string[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ScannedPackageResult {
|
|
27
|
-
files: {
|
|
28
|
-
/** Complete list of files for the given package */
|
|
29
|
-
list: string[];
|
|
30
|
-
/** Complete list of extensions (.js, .md etc.) */
|
|
31
|
-
extensions: string[];
|
|
32
|
-
/** List of minified javascript files */
|
|
33
|
-
minified: string[];
|
|
34
|
-
};
|
|
35
|
-
/** Size of the directory in bytes */
|
|
36
|
-
directorySize: number;
|
|
37
|
-
/** Unique license contained in the tarball (MIT, ISC ..) */
|
|
38
|
-
uniqueLicenseIds: string[];
|
|
39
|
-
/** All licenses with their SPDX */
|
|
40
|
-
licenses: ntlp.license[];
|
|
41
|
-
ast: {
|
|
42
|
-
dependencies: any;
|
|
43
|
-
warnings: any[];
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface ScanDirOrArchiveOptions {
|
|
48
|
-
ref: any;
|
|
49
|
-
locker: Locker;
|
|
50
|
-
tmpLocation: string;
|
|
51
|
-
logger: Logger;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface ScanFileOptions {
|
|
55
|
-
name: string;
|
|
56
|
-
ref: any;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function readManifest(dest: string, ref: any): Promise<ManifestData>;
|
|
60
|
-
export function scanFile(dest: string, file: string, options: ScanFileOptions): Promise<ScannedFileResult | null>;
|
|
61
|
-
export function scanPackage(dest: string, packageName
|
|
62
|
-
export function scanDirOrArchive(name: string, version: string, options: ScanDirOrArchiveOptions): Promise<void>;
|
|
63
|
-
}
|
|
1
|
+
import ntlp from "@nodesecure/ntlp";
|
|
2
|
+
import Locker from "@slimio/lock";
|
|
3
|
+
import Logger from "../src/logger.class";
|
|
4
|
+
|
|
5
|
+
export = tarball;
|
|
6
|
+
|
|
7
|
+
declare namespace tarball {
|
|
8
|
+
export interface ManifestData {
|
|
9
|
+
/** Dependencies in package.json */
|
|
10
|
+
packageDeps: string[];
|
|
11
|
+
/** DevDependencies in package.json */
|
|
12
|
+
packageDevDeps: string[];
|
|
13
|
+
/** Does package.json contain a 'gypfile' property ? */
|
|
14
|
+
packageGyp: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ScannedFileResult {
|
|
18
|
+
/** Dependencies in try/catch block (probably optional dependencies) */
|
|
19
|
+
inTryDeps: string[];
|
|
20
|
+
/** Dependencies required or imported */
|
|
21
|
+
dependencies: string[];
|
|
22
|
+
/** Required or imported javascript files */
|
|
23
|
+
filesDependencies: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ScannedPackageResult {
|
|
27
|
+
files: {
|
|
28
|
+
/** Complete list of files for the given package */
|
|
29
|
+
list: string[];
|
|
30
|
+
/** Complete list of extensions (.js, .md etc.) */
|
|
31
|
+
extensions: string[];
|
|
32
|
+
/** List of minified javascript files */
|
|
33
|
+
minified: string[];
|
|
34
|
+
};
|
|
35
|
+
/** Size of the directory in bytes */
|
|
36
|
+
directorySize: number;
|
|
37
|
+
/** Unique license contained in the tarball (MIT, ISC ..) */
|
|
38
|
+
uniqueLicenseIds: string[];
|
|
39
|
+
/** All licenses with their SPDX */
|
|
40
|
+
licenses: ntlp.license[];
|
|
41
|
+
ast: {
|
|
42
|
+
dependencies: any;
|
|
43
|
+
warnings: any[];
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ScanDirOrArchiveOptions {
|
|
48
|
+
ref: any;
|
|
49
|
+
locker: Locker;
|
|
50
|
+
tmpLocation: string;
|
|
51
|
+
logger: Logger;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ScanFileOptions {
|
|
55
|
+
name: string;
|
|
56
|
+
ref: any;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function readManifest(dest: string, ref: any): Promise<ManifestData>;
|
|
60
|
+
export function scanFile(dest: string, file: string, options: ScanFileOptions): Promise<ScannedFileResult | null>;
|
|
61
|
+
export function scanPackage(dest: string, packageName?: string): Promise<ScannedPackageResult>;
|
|
62
|
+
export function scanDirOrArchive(name: string, version: string, options: ScanDirOrArchiveOptions): Promise<void>;
|
|
63
|
+
}
|