@nodesecure/scanner 5.0.1 → 5.2.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 +3 -2
- package/package.json +17 -17
- package/src/depWalker.js +25 -4
- package/src/manifest.js +34 -5
- package/src/npmRegistry.js +57 -4
- package/src/tarball.js +27 -3
- package/src/utils/getLinks.js +31 -0
- package/src/utils/index.js +1 -0
- package/src/utils/semver.js +3 -2
- package/src/utils/warnings.js +1 -1
- package/types/scanner.d.ts +22 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Scorecard](https://api.securityscorecards.dev/projects/github.com/NodeSecure/sca
|
|
|
11
11
|
|
|
12
12
|
## Requirements
|
|
13
13
|
|
|
14
|
-
- [Node.js](https://nodejs.org/en/) version
|
|
14
|
+
- [Node.js](https://nodejs.org/en/) version 18 or higher
|
|
15
15
|
|
|
16
16
|
## Getting Started
|
|
17
17
|
|
|
@@ -78,7 +78,7 @@ interface Options {
|
|
|
78
78
|
## Contributors ✨
|
|
79
79
|
|
|
80
80
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
81
|
-
[](#contributors-)
|
|
82
82
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
83
83
|
|
|
84
84
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
@@ -102,6 +102,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
102
102
|
<td align="center" valign="top" width="14.28%"><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>
|
|
103
103
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Kawacrepe"><img src="https://avatars.githubusercontent.com/u/40260517?v=4?s=100" width="100px;" alt="Vincent Dhennin"/><br /><sub><b>Vincent Dhennin</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=Kawacrepe" title="Code">💻</a></td>
|
|
104
104
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fabnguess"><img src="https://avatars.githubusercontent.com/u/72697416?v=4?s=100" width="100px;" alt="Kouadio Fabrice Nguessan"/><br /><sub><b>Kouadio Fabrice Nguessan</b></sub></a><br /><a href="#maintenance-fabnguess" title="Maintenance">🚧</a></td>
|
|
105
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PierreDemailly"><img src="https://avatars.githubusercontent.com/u/39910767?v=4?s=100" width="100px;" alt="PierreDemailly"/><br /><sub><b>PierreDemailly</b></sub></a><br /><a href="https://github.com/NodeSecure/scanner/commits?author=PierreDemailly" title="Code">💻</a> <a href="https://github.com/NodeSecure/scanner/pulls?q=is%3Apr+reviewed-by%3APierreDemailly" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/NodeSecure/scanner/issues?q=author%3APierreDemailly" title="Bug reports">🐛</a> <a href="https://github.com/NodeSecure/scanner/commits?author=PierreDemailly" title="Tests">⚠️</a></td>
|
|
105
106
|
</tr>
|
|
106
107
|
</tbody>
|
|
107
108
|
</table>
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodesecure/scanner",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.2.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": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=18"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"lint": "eslint src test",
|
|
@@ -50,36 +50,36 @@
|
|
|
50
50
|
},
|
|
51
51
|
"homepage": "https://github.com/NodeSecure/scanner#readme",
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@nodesecure/eslint-config": "^1.
|
|
53
|
+
"@nodesecure/eslint-config": "^1.8.0",
|
|
54
54
|
"@slimio/is": "^2.0.0",
|
|
55
|
-
"@types/node": "^20.
|
|
56
|
-
"c8": "^
|
|
57
|
-
"dotenv": "^16.
|
|
58
|
-
"eslint": "
|
|
55
|
+
"@types/node": "^20.10.0",
|
|
56
|
+
"c8": "^8.0.1",
|
|
57
|
+
"dotenv": "^16.3.1",
|
|
58
|
+
"eslint": "8.37.0",
|
|
59
59
|
"get-folder-size": "^4.0.0",
|
|
60
|
-
"glob": "^10.3.
|
|
60
|
+
"glob": "^10.3.10",
|
|
61
61
|
"pkg-ok": "^3.0.0",
|
|
62
|
-
"sinon": "^
|
|
62
|
+
"sinon": "^17.0.1",
|
|
63
63
|
"snap-shot-core": "^10.2.4"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@nodesecure/authors": "^1.0.2",
|
|
67
67
|
"@nodesecure/flags": "^2.4.0",
|
|
68
68
|
"@nodesecure/fs-walk": "^1.0.0",
|
|
69
|
-
"@nodesecure/i18n": "^3.
|
|
70
|
-
"@nodesecure/js-x-ray": "^6.0
|
|
71
|
-
"@nodesecure/npm-registry-sdk": "^
|
|
69
|
+
"@nodesecure/i18n": "^3.5.0",
|
|
70
|
+
"@nodesecure/js-x-ray": "^6.3.0",
|
|
71
|
+
"@nodesecure/npm-registry-sdk": "^2.0.0",
|
|
72
72
|
"@nodesecure/ntlp": "^2.2.1",
|
|
73
73
|
"@nodesecure/vuln": "^1.7.0",
|
|
74
74
|
"@npm/types": "^1.0.2",
|
|
75
|
-
"@npmcli/arborist": "^
|
|
75
|
+
"@npmcli/arborist": "^7.2.1",
|
|
76
76
|
"@slimio/lock": "^1.0.0",
|
|
77
77
|
"builtins": "^5.0.1",
|
|
78
|
-
"combine-async-iterators": "^2.0
|
|
79
|
-
"itertools": "^2.1.
|
|
78
|
+
"combine-async-iterators": "^2.1.0",
|
|
79
|
+
"itertools": "^2.1.2",
|
|
80
80
|
"lodash.difference": "^4.5.0",
|
|
81
|
-
"pacote": "^
|
|
82
|
-
"semver": "^7.
|
|
81
|
+
"pacote": "^17.0.4",
|
|
82
|
+
"semver": "^7.5.4"
|
|
83
83
|
},
|
|
84
84
|
"type": "module"
|
|
85
85
|
}
|
package/src/depWalker.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
NPM_TOKEN
|
|
20
20
|
} from "./utils/index.js";
|
|
21
21
|
import { scanDirOrArchive } from "./tarball.js";
|
|
22
|
-
import { packageMetadata } from "./npmRegistry.js";
|
|
22
|
+
import { packageMetadata, manifestMetadata } from "./npmRegistry.js";
|
|
23
23
|
import Dependency from "./class/dependency.class.js";
|
|
24
24
|
import Logger from "./class/logger.class.js";
|
|
25
25
|
|
|
@@ -262,12 +262,13 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
|
|
|
262
262
|
const rootDepsOptions = { maxDepth, exclude, usePackageLock, fullLockMode, includeDevDeps, location, registry };
|
|
263
263
|
for await (const currentDep of getRootDependencies(manifest, rootDepsOptions)) {
|
|
264
264
|
const { name, version, dev } = currentDep;
|
|
265
|
+
|
|
265
266
|
const current = currentDep.exportAsPlainObject(name === manifest.name ? 0 : void 0);
|
|
266
267
|
let proceedDependencyAnalysis = true;
|
|
267
268
|
|
|
268
269
|
if (dependencies.has(name)) {
|
|
269
|
-
// TODO: how to handle different metadata ?
|
|
270
270
|
const dep = dependencies.get(name);
|
|
271
|
+
promisesToWait.push(manifestMetadata(name, version, dep.metadata));
|
|
271
272
|
|
|
272
273
|
const currVersion = Object.keys(current.versions)[0];
|
|
273
274
|
if (currVersion in dep.versions) {
|
|
@@ -292,7 +293,7 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
|
|
|
292
293
|
logger.tick(ScannerLoggerEvents.analysis.tree);
|
|
293
294
|
|
|
294
295
|
// There is no need to fetch 'N' times the npm metadata for the same package.
|
|
295
|
-
if (fetchedMetadataPackages.has(name)) {
|
|
296
|
+
if (fetchedMetadataPackages.has(name) || !current.versions[version].existOnRemoteRegistry) {
|
|
296
297
|
logger.tick(ScannerLoggerEvents.analysis.registry);
|
|
297
298
|
}
|
|
298
299
|
else {
|
|
@@ -333,7 +334,27 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
|
|
|
333
334
|
|
|
334
335
|
// We do this because it "seem" impossible to link all dependencies in the first walk.
|
|
335
336
|
// Because we are dealing with package only one time it may happen sometimes.
|
|
337
|
+
const globalWarnings = [];
|
|
336
338
|
for (const [packageName, dependency] of dependencies) {
|
|
339
|
+
const metadataIntegrities = dependency.metadata?.integrity ?? {};
|
|
340
|
+
|
|
341
|
+
for (const [version, integrity] of Object.entries(metadataIntegrities)) {
|
|
342
|
+
const dependencyVer = dependency.versions[version];
|
|
343
|
+
|
|
344
|
+
const isEmptyPackage = dependencyVer.warnings
|
|
345
|
+
.some((warning) => warning.kind === "empty-package");
|
|
346
|
+
if (isEmptyPackage) {
|
|
347
|
+
globalWarnings.push(`${packageName}@${version} only contain a package.json file!`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (!("integrity" in dependencyVer) || dependencyVer.flags.includes("isGit")) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (dependencyVer.integrity !== integrity) {
|
|
355
|
+
globalWarnings.push(`${packageName}@${version} manifest & tarball integrity doesn't match!`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
337
358
|
for (const [verStr, verDescriptor] of Object.entries(dependency.versions)) {
|
|
338
359
|
verDescriptor.flags.push(...addMissingVersionFlags(new Set(verDescriptor.flags), dependency));
|
|
339
360
|
|
|
@@ -352,7 +373,7 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
|
|
|
352
373
|
|
|
353
374
|
try {
|
|
354
375
|
const { warnings, flaggedAuthors } = await getDependenciesWarnings(dependencies);
|
|
355
|
-
payload.warnings = warnings;
|
|
376
|
+
payload.warnings = globalWarnings.concat(warnings);
|
|
356
377
|
payload.flaggedAuthors = flaggedAuthors;
|
|
357
378
|
payload.dependencies = Object.fromEntries(dependencies);
|
|
358
379
|
|
package/src/manifest.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Import Node.js Dependencies
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import crypto from "node:crypto";
|
|
4
5
|
|
|
5
6
|
// Import Internal Dependencies
|
|
6
7
|
import { parseAuthor } from "./utils/index.js";
|
|
@@ -10,6 +11,7 @@ import { parseAuthor } from "./utils/index.js";
|
|
|
10
11
|
const kNativeNpmPackages = new Set([
|
|
11
12
|
"node-gyp", "node-pre-gyp", "node-gyp-build", "node-addon-api"
|
|
12
13
|
]);
|
|
14
|
+
const kNodemodulesBinPrefix = "node_modules/.bin/";
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* @see https://www.nerdycode.com/prevent-npm-executing-scripts-security/
|
|
@@ -24,7 +26,7 @@ const kUnsafeNpmScripts = new Set([
|
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* @param {!string} location
|
|
27
|
-
* @returns {import("@npm/types").PackageJson}
|
|
29
|
+
* @returns {Promise<import("@npm/types").PackageJson>}
|
|
28
30
|
*/
|
|
29
31
|
export async function read(location) {
|
|
30
32
|
const packageStr = await fs.readFile(
|
|
@@ -37,13 +39,39 @@ export async function read(location) {
|
|
|
37
39
|
|
|
38
40
|
export async function readAnalyze(location) {
|
|
39
41
|
const {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
name,
|
|
43
|
+
version,
|
|
44
|
+
description = "",
|
|
45
|
+
author = {},
|
|
46
|
+
scripts = {},
|
|
47
|
+
dependencies = {},
|
|
48
|
+
devDependencies = {},
|
|
49
|
+
gypfile = false,
|
|
42
50
|
engines = {},
|
|
43
51
|
repository = {},
|
|
44
|
-
imports = {}
|
|
52
|
+
imports = {},
|
|
53
|
+
license = ""
|
|
45
54
|
} = await read(location);
|
|
46
55
|
|
|
56
|
+
for (const [scriptName, scriptValue] of Object.entries(scripts)) {
|
|
57
|
+
if (scriptValue.startsWith(kNodemodulesBinPrefix)) {
|
|
58
|
+
scripts[scriptName] = scriptValue.replaceAll(kNodemodulesBinPrefix, "");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const integrityObj = {
|
|
63
|
+
name,
|
|
64
|
+
version,
|
|
65
|
+
dependencies,
|
|
66
|
+
license,
|
|
67
|
+
scripts
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const integrity = crypto
|
|
71
|
+
.createHash("sha256")
|
|
72
|
+
.update(JSON.stringify(integrityObj))
|
|
73
|
+
.digest("hex");
|
|
74
|
+
|
|
47
75
|
const packageDeps = Object.keys(dependencies);
|
|
48
76
|
const packageDevDeps = Object.keys(devDependencies);
|
|
49
77
|
const hasNativePackage = [...packageDevDeps, ...packageDeps]
|
|
@@ -60,6 +88,7 @@ export async function readAnalyze(location) {
|
|
|
60
88
|
packageDeps,
|
|
61
89
|
packageDevDeps,
|
|
62
90
|
nodejs: { imports },
|
|
63
|
-
hasNativeElements: hasNativePackage || gypfile
|
|
91
|
+
hasNativeElements: hasNativePackage || gypfile,
|
|
92
|
+
integrity
|
|
64
93
|
};
|
|
65
94
|
}
|
package/src/npmRegistry.js
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
+
// Import Node.js Dependencies
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
|
|
1
4
|
// Import Third-party Dependencies
|
|
2
5
|
import semver from "semver";
|
|
3
|
-
import { packument } from "@nodesecure/npm-registry-sdk";
|
|
6
|
+
import { packument, packumentVersion } from "@nodesecure/npm-registry-sdk";
|
|
4
7
|
|
|
5
8
|
// Import Internal Dependencies
|
|
6
|
-
import { parseAuthor } from "./utils/index.js";
|
|
9
|
+
import { parseAuthor, getLinks } from "./utils/index.js";
|
|
10
|
+
|
|
11
|
+
export async function manifestMetadata(name, version, metadata) {
|
|
12
|
+
try {
|
|
13
|
+
const pkgVersion = await packumentVersion(name, version);
|
|
14
|
+
|
|
15
|
+
const integrity = getPackumentVersionIntegrity(pkgVersion);
|
|
16
|
+
metadata.integrity[version] = integrity;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Ignore
|
|
20
|
+
}
|
|
21
|
+
}
|
|
7
22
|
|
|
8
23
|
export async function packageMetadata(name, version, options) {
|
|
9
24
|
const { ref, logger } = options;
|
|
25
|
+
const packageSpec = `${name}:${version}`;
|
|
10
26
|
|
|
11
27
|
try {
|
|
12
28
|
const pkg = await packument(name);
|
|
@@ -24,18 +40,32 @@ export async function packageMetadata(name, version, options) {
|
|
|
24
40
|
lastUpdateAt,
|
|
25
41
|
hasReceivedUpdateInOneYear: !(oneYearFromToday > lastUpdateAt),
|
|
26
42
|
maintainers: pkg.maintainers ?? [],
|
|
27
|
-
publishers: []
|
|
43
|
+
publishers: [],
|
|
44
|
+
integrity: {}
|
|
28
45
|
};
|
|
29
46
|
|
|
30
47
|
const isOutdated = semver.neq(version, lastVersion);
|
|
48
|
+
const flags = ref.versions[version].flags;
|
|
31
49
|
if (isOutdated) {
|
|
32
|
-
|
|
50
|
+
flags.push("isOutdated");
|
|
33
51
|
}
|
|
34
52
|
|
|
35
53
|
const publishers = new Set();
|
|
36
54
|
let searchForMaintainersInVersions = metadata.maintainers.length === 0;
|
|
37
55
|
for (const ver of Object.values(pkg.versions).reverse()) {
|
|
56
|
+
const versionSpec = `${ver.name}:${ver.version}`;
|
|
57
|
+
if (packageSpec === versionSpec) {
|
|
58
|
+
if (ver.deprecated && !flags.includes("isDeprecated")) {
|
|
59
|
+
flags.push("isDeprecated");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
metadata.integrity[ver.version] = getPackumentVersionIntegrity(
|
|
63
|
+
ver
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
38
67
|
const { _npmUser: npmUser, version, maintainers = [] } = ver;
|
|
68
|
+
|
|
39
69
|
const isNullOrUndefined = typeof npmUser === "undefined" || npmUser === null;
|
|
40
70
|
if (isNullOrUndefined || !("name" in npmUser) || typeof npmUser.name !== "string") {
|
|
41
71
|
continue;
|
|
@@ -61,6 +91,7 @@ export async function packageMetadata(name, version, options) {
|
|
|
61
91
|
}
|
|
62
92
|
}
|
|
63
93
|
|
|
94
|
+
Object.assign(ref.versions[version], { links: getLinks(pkg.versions[version]) });
|
|
64
95
|
Object.assign(ref.metadata, metadata);
|
|
65
96
|
}
|
|
66
97
|
catch {
|
|
@@ -70,3 +101,25 @@ export async function packageMetadata(name, version, options) {
|
|
|
70
101
|
logger.tick("registry");
|
|
71
102
|
}
|
|
72
103
|
}
|
|
104
|
+
|
|
105
|
+
function getPackumentVersionIntegrity(packumentVersion) {
|
|
106
|
+
const { name, version, dependencies = {}, license = "", scripts = {} } = packumentVersion;
|
|
107
|
+
|
|
108
|
+
// See https://github.com/npm/cli/issues/5234
|
|
109
|
+
if ("install" in dependencies && dependencies.install === "node-gyp rebuild") {
|
|
110
|
+
delete dependencies.install;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const integrityObj = {
|
|
114
|
+
name,
|
|
115
|
+
version,
|
|
116
|
+
dependencies,
|
|
117
|
+
license,
|
|
118
|
+
scripts
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return crypto
|
|
122
|
+
.createHash("sha256")
|
|
123
|
+
.update(JSON.stringify(integrityObj))
|
|
124
|
+
.digest("hex");
|
|
125
|
+
}
|
package/src/tarball.js
CHANGED
|
@@ -67,18 +67,42 @@ export async function scanDirOrArchive(name, version, options) {
|
|
|
67
67
|
});
|
|
68
68
|
await timers.setImmediate();
|
|
69
69
|
}
|
|
70
|
+
else {
|
|
71
|
+
// Set links to an empty object because theses are generated only for NPM tarballs
|
|
72
|
+
Object.assign(ref, { links: {} });
|
|
73
|
+
}
|
|
70
74
|
|
|
71
75
|
// Read the package.json at the root of the directory or archive.
|
|
72
76
|
const {
|
|
73
77
|
packageDeps,
|
|
74
78
|
packageDevDeps,
|
|
75
|
-
author,
|
|
76
|
-
|
|
79
|
+
author,
|
|
80
|
+
description,
|
|
81
|
+
hasScript,
|
|
82
|
+
hasNativeElements,
|
|
83
|
+
nodejs,
|
|
84
|
+
engines,
|
|
85
|
+
repository,
|
|
86
|
+
scripts,
|
|
87
|
+
integrity
|
|
77
88
|
} = await manifest.readAnalyze(dest);
|
|
78
|
-
Object.assign(ref, {
|
|
89
|
+
Object.assign(ref, {
|
|
90
|
+
author, description, engines, repository, scripts, integrity
|
|
91
|
+
});
|
|
79
92
|
|
|
80
93
|
// Get the composition of the (extracted) directory
|
|
81
94
|
const { ext, files, size } = await getTarballComposition(dest);
|
|
95
|
+
if (files.length === 1 && files.includes("package.json")) {
|
|
96
|
+
ref.warnings.push({
|
|
97
|
+
kind: "empty-package",
|
|
98
|
+
location: null,
|
|
99
|
+
i18n: "sast_warnings.emptyPackage",
|
|
100
|
+
severity: "Critical",
|
|
101
|
+
source: "Scanner",
|
|
102
|
+
experimental: false
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
82
106
|
ref.size = size;
|
|
83
107
|
ref.composition.extensions.push(...ext);
|
|
84
108
|
ref.composition.files.push(...files);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// CONSTANTS
|
|
2
|
+
const kVCSHosts = new Set(["github.com", "gitlab.com"]);
|
|
3
|
+
|
|
4
|
+
function getVCSRepositoryURL(link) {
|
|
5
|
+
try {
|
|
6
|
+
const url = new URL(link);
|
|
7
|
+
const { hostname, pathname } = url;
|
|
8
|
+
|
|
9
|
+
if (kVCSHosts.has(hostname) === false) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const [owner, repo] = pathname.split("/").filter(Boolean).map((curr) => curr.replace(".git", ""));
|
|
14
|
+
|
|
15
|
+
return `https://${hostname}/${owner}/${repo}`;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getLinks(pkg) {
|
|
23
|
+
const homepage = pkg.homepage || null;
|
|
24
|
+
const repositoryUrl = pkg.repository?.url || null;
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
npm: `https://www.npmjs.com/package/${pkg.name}/v/${pkg.version}`,
|
|
28
|
+
homepage,
|
|
29
|
+
repository: getVCSRepositoryURL(homepage) ?? getVCSRepositoryURL(repositoryUrl)
|
|
30
|
+
};
|
|
31
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./analyzeDependencies.js";
|
|
|
11
11
|
export * from "./booleanToFlags.js";
|
|
12
12
|
export * from "./addMissingVersionFlags.js";
|
|
13
13
|
export * from "./parseManifestAuthor.js";
|
|
14
|
+
export * from "./getLinks.js";
|
|
14
15
|
|
|
15
16
|
export const NPM_TOKEN = typeof process.env.NODE_SECURE_TOKEN === "string" ?
|
|
16
17
|
{ token: process.env.NODE_SECURE_TOKEN } :
|
package/src/utils/semver.js
CHANGED
|
@@ -50,12 +50,13 @@ export async function getCleanDependencyName([depName, range]) {
|
|
|
50
50
|
|
|
51
51
|
export function getSemVerWarning(value) {
|
|
52
52
|
return {
|
|
53
|
-
kind: "
|
|
53
|
+
kind: "zero-semver",
|
|
54
54
|
file: "package.json",
|
|
55
55
|
value,
|
|
56
56
|
location: null,
|
|
57
|
-
i18n: "sast_warnings.
|
|
57
|
+
i18n: "sast_warnings.zeroSemVer",
|
|
58
58
|
severity: "Information",
|
|
59
|
+
source: "Scanner",
|
|
59
60
|
experimental: false
|
|
60
61
|
};
|
|
61
62
|
}
|
package/src/utils/warnings.js
CHANGED
|
@@ -8,7 +8,7 @@ import { extractAllAuthors } from "@nodesecure/authors";
|
|
|
8
8
|
// Import Internal Dependencies
|
|
9
9
|
import { getDirNameFromUrl } from "./dirname.js";
|
|
10
10
|
|
|
11
|
-
i18n.extendFromSystemPath(
|
|
11
|
+
await i18n.extendFromSystemPath(
|
|
12
12
|
path.join(getDirNameFromUrl(import.meta.url), "..", "..", "i18n")
|
|
13
13
|
);
|
|
14
14
|
|
package/types/scanner.d.ts
CHANGED
|
@@ -40,6 +40,15 @@ declare namespace Scanner {
|
|
|
40
40
|
at: string;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export interface DependencyLinks {
|
|
44
|
+
/** NPM Registry page */
|
|
45
|
+
npm: string;
|
|
46
|
+
/** Homepage URL */
|
|
47
|
+
homepage?: string;
|
|
48
|
+
/** VCS repository URL */
|
|
49
|
+
repository?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
export interface DependencyVersion {
|
|
44
53
|
/** Id of the package (useful for usedBy relation) */
|
|
45
54
|
id: number;
|
|
@@ -104,6 +113,14 @@ declare namespace Scanner {
|
|
|
104
113
|
* If the dependency is a GIT repository
|
|
105
114
|
*/
|
|
106
115
|
gitUrl: null | string;
|
|
116
|
+
/**
|
|
117
|
+
* Version MD5 integrity hash
|
|
118
|
+
* Generated by the scanner to verify manifest/tarball confusion
|
|
119
|
+
*
|
|
120
|
+
* (Not supported on GIT dependency)
|
|
121
|
+
*/
|
|
122
|
+
integrity?: string;
|
|
123
|
+
links: DependencyLinks;
|
|
107
124
|
}
|
|
108
125
|
|
|
109
126
|
export interface Dependency {
|
|
@@ -131,6 +148,11 @@ declare namespace Scanner {
|
|
|
131
148
|
* List of people who published this package
|
|
132
149
|
*/
|
|
133
150
|
publishers: Publisher[];
|
|
151
|
+
/**
|
|
152
|
+
* Version MD5 integrity hash
|
|
153
|
+
* Generated by the scanner to verify manifest/tarball confusion
|
|
154
|
+
*/
|
|
155
|
+
integrity: Record<string, string>;
|
|
134
156
|
}
|
|
135
157
|
/** List of versions of this package available in the dependency tree (In the payload) */
|
|
136
158
|
versions: Record<string, DependencyVersion>;
|