@nodesecure/scanner 3.0.0 → 3.2.1

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/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import Scanner from "./types/scanner";
2
- import { cwd, from, verify } from "./types/api";
2
+ import { cwd, from, verify, ScannerLoggerEvents } from "./types/api";
3
3
  import { depWalker } from "./types/walker";
4
4
  import { Logger, LoggerEventData } from "./types/logger";
5
5
  import tarball from "./types/tarball";
6
6
 
7
7
  export {
8
- cwd, from, verify,
8
+ cwd, from, verify, ScannerLoggerEvents,
9
9
  Scanner,
10
10
  Logger,
11
11
  LoggerEventData,
package/index.js CHANGED
@@ -11,6 +11,7 @@ import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk";
11
11
  // Import Internal Dependencies
12
12
  import { depWalker } from "./src/depWalker.js";
13
13
  import { NPM_TOKEN } from "./src/utils/index.js";
14
+ import { ScannerLoggerEvents } from "./src/constants.js";
14
15
  import Logger from "./src/class/logger.class.js";
15
16
  import * as tarball from "./src/tarball.js";
16
17
 
@@ -20,20 +21,20 @@ const kDefaultCwdOptions = { forceRootAnalysis: true, usePackageLock: true };
20
21
  export async function cwd(cwd = process.cwd(), options = {}, logger = new Logger()) {
21
22
  const finalizedOptions = Object.assign({}, kDefaultCwdOptions, options);
22
23
 
23
- logger.start("readManifest");
24
+ logger.start(ScannerLoggerEvents.manifest.read);
24
25
  const packagePath = path.join(cwd, "package.json");
25
26
  const str = await fs.readFile(packagePath, "utf-8");
26
- logger.end("readManifest");
27
+ logger.end(ScannerLoggerEvents.manifest.read);
27
28
 
28
29
  return depWalker(JSON.parse(str), finalizedOptions, logger);
29
30
  }
30
31
 
31
32
  export async function from(packageName, options, logger = new Logger()) {
32
- logger.start("fetchManifest");
33
+ logger.start(ScannerLoggerEvents.manifest.fetch);
33
34
  const manifest = await pacote.manifest(packageName, {
34
35
  ...NPM_TOKEN, registry: getLocalRegistryURL(), cache: `${os.homedir()}/.npm`
35
36
  });
36
- logger.end("fetchManifest");
37
+ logger.end(ScannerLoggerEvents.manifest.fetch);
37
38
 
38
39
  return depWalker(manifest, options, logger);
39
40
  }
@@ -59,4 +60,4 @@ export async function verify(packageName = null) {
59
60
  }
60
61
  }
61
62
 
62
- export { depWalker, tarball, Logger };
63
+ export { depWalker, tarball, Logger, ScannerLoggerEvents };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodesecure/scanner",
3
- "version": "3.0.0",
3
+ "version": "3.2.1",
4
4
  "description": "A package API to run a static analysis of your module's dependencies.",
5
5
  "exports": "./index.js",
6
6
  "engines": {
@@ -0,0 +1,13 @@
1
+
2
+ export const ScannerLoggerEvents = {
3
+ done: "depWalkerFinished",
4
+ analysis: {
5
+ tree: "walkTree",
6
+ tarball: "tarball",
7
+ registry: "registry"
8
+ },
9
+ manifest: {
10
+ read: "readManifest",
11
+ fetch: "fetchManifest"
12
+ }
13
+ };
package/src/depWalker.js CHANGED
@@ -10,8 +10,9 @@ import iter from "itertools";
10
10
  import pacote from "pacote";
11
11
  import Arborist from "@npmcli/arborist";
12
12
  import Lock from "@slimio/lock";
13
- import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk";
14
13
  import * as vuln from "@nodesecure/vuln";
14
+ import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk";
15
+ import { ScannerLoggerEvents } from "./constants.js";
15
16
 
16
17
  // Import Internal Dependencies
17
18
  import {
@@ -134,7 +135,7 @@ export async function* getRootDependencies(manifest, options) {
134
135
  tree = await arb.loadVirtual();
135
136
  }
136
137
 
137
- iterators = iter.filter(tree.edgesOut.entries(), ([, { to }]) => !to.dev)
138
+ iterators = iter.filter(tree.edgesOut.entries(), ([, { to }]) => to !== null && !to.dev)
138
139
  .map(([packageName, { to }]) => deepReadEdges(packageName, { to, parent, fullLockMode, exclude }));
139
140
  }
140
141
  else {
@@ -183,8 +184,8 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
183
184
 
184
185
  const payload = {
185
186
  id: tmpLocation.slice(-6),
186
- rootDepencyName: manifest.name,
187
- version: packageVersion,
187
+ rootDependencyName: manifest.name,
188
+ scannerVersion: packageVersion,
188
189
  vulnerabilityStrategy,
189
190
  warnings: []
190
191
  };
@@ -194,12 +195,15 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
194
195
  const dependencies = new Map();
195
196
 
196
197
  {
197
- logger.start("walkTree").start("tarball").start("registry");
198
+ logger
199
+ .start(ScannerLoggerEvents.analysis.tree)
200
+ .start(ScannerLoggerEvents.analysis.tarball)
201
+ .start(ScannerLoggerEvents.analysis.registry);
198
202
  const fetchedMetadataPackages = new Set();
199
203
  const promisesToWait = [];
200
204
 
201
205
  const tarballLocker = new Lock({ maxConcurrent: 5 });
202
- tarballLocker.on("freeOne", () => logger.tick("tarball"));
206
+ tarballLocker.on("freeOne", () => logger.tick(ScannerLoggerEvents.analysis.tarball));
203
207
 
204
208
  const rootDepsOptions = { maxDepth, exclude, usePackageLock, fullLockMode };
205
209
  for await (const currentDep of getRootDependencies(manifest, rootDepsOptions)) {
@@ -226,11 +230,11 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
226
230
  }
227
231
 
228
232
  if (proceedDependencyAnalysis) {
229
- logger.tick("walkTree");
233
+ logger.tick(ScannerLoggerEvents.analysis.tree);
230
234
 
231
235
  // There is no need to fetch 'N' times the npm metadata for the same package.
232
236
  if (fetchedMetadataPackages.has(name)) {
233
- logger.tick("registry");
237
+ logger.tick(ScannerLoggerEvents.analysis.registry);
234
238
  }
235
239
  else {
236
240
  fetchedMetadataPackages.add(name);
@@ -249,13 +253,13 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
249
253
  }
250
254
  }
251
255
 
252
- logger.end("walkTree");
256
+ logger.end(ScannerLoggerEvents.analysis.tree);
253
257
 
254
258
  // Wait for all extraction to be done!
255
259
  await Promise.allSettled(promisesToWait);
256
260
  await timers.setImmediate();
257
261
 
258
- logger.end("tarball").end("registry");
262
+ logger.end(ScannerLoggerEvents.analysis.tarball).end(ScannerLoggerEvents.analysis.registry);
259
263
  }
260
264
 
261
265
  const { hydratePayloadDependencies, strategy } = await vuln.setStrategy(vulnerabilityStrategy);
@@ -294,5 +298,7 @@ export async function depWalker(manifest, options = {}, logger = new Logger()) {
294
298
  finally {
295
299
  await timers.setImmediate();
296
300
  await fs.rm(tmpLocation, { recursive: true, force: true });
301
+
302
+ logger.emit(ScannerLoggerEvents.done);
297
303
  }
298
304
  }
@@ -28,7 +28,7 @@ export function analyzeDependencies(dependencies, deps = {}) {
28
28
 
29
29
  return {
30
30
  nodeDependencies,
31
- thirdPartyDependencies,
31
+ thirdPartyDependencies: [...new Set(thirdPartyDependencies)],
32
32
  unusedDependencies,
33
33
  missingDependencies,
34
34
 
@@ -8,6 +8,7 @@ const kWarningsMessages = Object.freeze({
8
8
  iohook: getToken("warnings.keylogging")
9
9
  });
10
10
  const kPackages = new Set(Object.keys(kWarningsMessages));
11
+ const kAuthors = new Set(["marak", "marak.squires@gmail.com"]);
11
12
 
12
13
  function getWarning(depName) {
13
14
  return `${kDetectedDep(depName)} ${kWarningsMessages[depName]}`;
@@ -21,6 +22,15 @@ export function getDependenciesWarnings(dependencies) {
21
22
  }
22
23
  }
23
24
 
25
+ // TODO: optimize with @nodesecure/author later
26
+ for (const [packageName, dependency] of dependencies) {
27
+ for (const { name, email } of dependency.metadata.maintainers) {
28
+ if (kAuthors.has(name) || kAuthors.has(email)) {
29
+ warnings.push(`'Marak Squires' package '${packageName}' has been detected in the dependency tree`);
30
+ }
31
+ }
32
+ }
33
+
24
34
  return warnings;
25
35
  }
26
36
 
package/types/api.d.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  import Scanner from "./scanner";
2
- import { Logger } from "./logger";
2
+ import { Logger, LoggerEvents } from "./logger";
3
3
 
4
4
  export {
5
5
  cwd,
6
6
  from,
7
- verify
7
+ verify,
8
+ ScannerLoggerEvents
8
9
  }
9
10
 
11
+ declare const ScannerLoggerEvents: LoggerEvents;
12
+
10
13
  declare function cwd(path: string, options?: Scanner.Options, logger?: Logger): Promise<Scanner.Payload>;
11
14
  declare function from(packageName: string, options?: Scanner.Options, logger?: Logger): Promise<Scanner.Payload>;
12
15
  declare function verify(packageName: string): Promise<Scanner.VerifyPayload>;
package/types/logger.d.ts CHANGED
@@ -2,7 +2,21 @@ import { EventEmitter } from "events";
2
2
 
3
3
  export {
4
4
  Logger,
5
- LoggerEventData
5
+ LoggerEventData,
6
+ LoggerEvents
7
+ }
8
+
9
+ interface LoggerEvents {
10
+ readonly done: "depWalkerFinished";
11
+ readonly analysis: {
12
+ readonly tree: "walkTree";
13
+ readonly tarball: "tarball";
14
+ readonly registry: "registry";
15
+ };
16
+ readonly manifest: {
17
+ readonly read: "readManifest";
18
+ readonly fetch: "fetchManifest";
19
+ };
6
20
  }
7
21
 
8
22
  interface LoggerEventData {
@@ -1,182 +1,185 @@
1
- // Import NodeSecure Dependencies
2
- import * as JSXRay from "@nodesecure/js-x-ray";
3
- import { license as License } from "@nodesecure/ntlp";
4
- import * as Vuln from "@nodesecure/vuln";
5
- import { Flags } from "@nodesecure/flags";
6
-
7
- // Import Third-party Dependencies
8
- import { Maintainer } from "@npm/types";
9
-
10
- export = Scanner;
11
-
12
- declare namespace Scanner {
13
- export interface Publisher {
14
- /**
15
- * Publisher npm user name.
16
- */
17
- name: string;
18
- /**
19
- * Publisher npm user email.
20
- */
21
- email: string;
22
- /**
23
- * First version published.
24
- */
25
- version: string;
26
- /**
27
- * Date of the first publication
28
- * @example 2021-08-10T20:45:08.342Z
29
- */
30
- at: string;
31
- }
32
-
33
- export interface DependencyVersion {
34
- /** Id of the package (useful for usedBy relation) */
35
- id: number;
36
- /** By whom (id) is used the package */
37
- usedBy: Record<string, string>;
38
- /** Size on disk of the extracted tarball (in bytes) */
39
- size: number;
40
- /** Package description */
41
- description: string;
42
- /** Author of the package. This information is not trustable and can be empty. */
43
- author: Maintainer;
44
- /**
45
- * JS-X-Ray warnings
46
- *
47
- * @see https://github.com/NodeSecure/js-x-ray/blob/master/WARNINGS.md
48
- */
49
- warnings: JSXRay.Warning<JSXRay.BaseWarning>[];
50
- /** Tarball composition (files and dependencies) */
51
- composition: {
52
- /** Files extensions (.js, .md, .exe etc..) */
53
- extensions: string[];
54
- files: string[];
55
- /** Minified files (foo.min.js etc..) */
56
- minified: string[];
57
- required_files: string[];
58
- required_thirdparty: string[];
59
- required_nodejs: string[];
60
- unused: string[];
61
- missing: string[];
62
- };
63
- /**
64
- * Package licenses with SPDX expression.
65
- *
66
- * @see https://github.com/NodeSecure/licenses-conformance
67
- * @see https://github.com/NodeSecure/npm-tarball-license-parser
68
- */
69
- license: License[];
70
- /**
71
- * Flags (Array of string)
72
- *
73
- * @see https://github.com/NodeSecure/flags/blob/main/FLAGS.md
74
- */
75
- flags: Flags[];
76
- /**
77
- * If the dependency is a GIT repository
78
- */
79
- gitUrl: null | string;
80
- }
81
-
82
- export interface Dependency {
83
- /** NPM Registry metadata */
84
- metadata: {
85
- /** Count of dependencies */
86
- dependencyCount: number;
87
- /** Number of releases published on npm */
88
- publishedCount: number;
89
- lastUpdateAt: number;
90
- /** Last version SemVer */
91
- lastVersion: number;
92
- hasChangedAuthor: boolean;
93
- hasManyPublishers: boolean;
94
- hasReceivedUpdateInOneYear: boolean;
95
- /** Author of the package. This information is not trustable and can be empty. */
96
- author: Maintainer;
97
- /** Package home page */
98
- homepage: string | null;
99
- /**
100
- * List of maintainers (list of people in the organization related to the package)
101
- */
102
- maintainers: { name: string, email: string }[];
103
- /**
104
- * List of people who published this package
105
- */
106
- publishers: Publisher[];
107
- }
108
- /** List of versions of this package available in the dependency tree (In the payload) */
109
- versions: Record<string, DependencyVersion>;
110
- /**
111
- * Vulnerabilities fetched dependending on the selected vulnerabilityStrategy
112
- *
113
- * @see https://github.com/NodeSecure/vuln
114
- */
115
- vulnerabilities: Vuln.Strategy.StandardVulnerability[];
116
- }
117
-
118
- export interface Payload {
119
- /** Payload unique id */
120
- id: string;
121
- /** Name of the analyzed package */
122
- rootDependencyName: string;
123
- /** Global warnings list */
124
- warnings: [];
125
- /** All the dependencies of the package (flattened) */
126
- dependencies: Record<string, Dependency>;
127
- /** Version of the scanner used to generate the result */
128
- version: string;
129
- /** Vulnerability strategy name (npm, snyk, node) */
130
- vulnerabilityStrategy: Vuln.Strategy.Kind;
131
- }
132
-
133
- export interface VerifyPayload {
134
- files: {
135
- list: string[];
136
- extensions: string[];
137
- minified: string[];
138
- };
139
- directorySize: number;
140
- uniqueLicenseIds: string[];
141
- licenses: License[];
142
- ast: {
143
- dependencies: Record<string, JSXRay.Dependency>;
144
- warnings: JSXRay.Warning<JSXRay.BaseWarning>[];
145
- };
146
- }
147
-
148
- export interface Options {
149
- /**
150
- * Maximum tree depth
151
- *
152
- * @default 4
153
- */
154
- readonly maxDepth?: number;
155
- /**
156
- * Use root package-lock.json. This will have the effect of triggering the Arborist package.
157
- *
158
- * @default false for from() API
159
- * @default true for cwd() API
160
- */
161
- readonly usePackageLock?: boolean;
162
- /**
163
- * Vulnerability strategy name (npm, snyk, node)
164
- *
165
- * @default NONE
166
- */
167
- readonly vulnerabilityStrategy: Vuln.Strategy.Kind;
168
- /**
169
- * Analyze root package.
170
- *
171
- * @default false for from() API
172
- * @default true for cwd() API
173
- */
174
- readonly forceRootAnalysis?: boolean;
175
- /**
176
- * Deeper dependencies analysis with cwd() API.
177
- *
178
- * @default false
179
- */
180
- readonly fullLockMode?: boolean;
181
- }
182
- }
1
+ // Import NodeSecure Dependencies
2
+ import * as JSXRay from "@nodesecure/js-x-ray";
3
+ import { license as License } from "@nodesecure/ntlp";
4
+ import * as Vuln from "@nodesecure/vuln";
5
+ import { Flags } from "@nodesecure/flags";
6
+
7
+ // Import Third-party Dependencies
8
+ import { Maintainer } from "@npm/types";
9
+
10
+ export = Scanner;
11
+
12
+ declare namespace Scanner {
13
+ export interface Publisher {
14
+ /**
15
+ * Publisher npm user name.
16
+ */
17
+ name: string;
18
+ /**
19
+ * Publisher npm user email.
20
+ */
21
+ email: string;
22
+ /**
23
+ * First version published.
24
+ */
25
+ version: string;
26
+ /**
27
+ * Date of the first publication
28
+ * @example 2021-08-10T20:45:08.342Z
29
+ */
30
+ at: string;
31
+ }
32
+
33
+ export interface DependencyVersion {
34
+ /** Id of the package (useful for usedBy relation) */
35
+ id: number;
36
+ /** By whom (id) is used the package */
37
+ usedBy: Record<string, string>;
38
+ /** Size on disk of the extracted tarball (in bytes) */
39
+ size: number;
40
+ /** Package description */
41
+ description: string;
42
+ /** Author of the package. This information is not trustable and can be empty. */
43
+ author: Maintainer;
44
+ /**
45
+ * JS-X-Ray warnings
46
+ *
47
+ * @see https://github.com/NodeSecure/js-x-ray/blob/master/WARNINGS.md
48
+ */
49
+ warnings: JSXRay.Warning<JSXRay.BaseWarning>[];
50
+ /** Tarball composition (files and dependencies) */
51
+ composition: {
52
+ /** Files extensions (.js, .md, .exe etc..) */
53
+ extensions: string[];
54
+ files: string[];
55
+ /** Minified files (foo.min.js etc..) */
56
+ minified: string[];
57
+ required_files: string[];
58
+ required_thirdparty: string[];
59
+ required_nodejs: string[];
60
+ unused: string[];
61
+ missing: string[];
62
+ };
63
+ /**
64
+ * Package licenses with SPDX expression.
65
+ *
66
+ * @see https://github.com/NodeSecure/licenses-conformance
67
+ * @see https://github.com/NodeSecure/npm-tarball-license-parser
68
+ */
69
+ license: License[];
70
+ /**
71
+ * Flags (Array of string)
72
+ *
73
+ * @see https://github.com/NodeSecure/flags/blob/main/FLAGS.md
74
+ */
75
+ flags: Flags[];
76
+ /**
77
+ * If the dependency is a GIT repository
78
+ */
79
+ gitUrl: null | string;
80
+ }
81
+
82
+ export interface Dependency {
83
+ /** NPM Registry metadata */
84
+ metadata: {
85
+ /** Count of dependencies */
86
+ dependencyCount: number;
87
+ /** Number of releases published on npm */
88
+ publishedCount: number;
89
+ lastUpdateAt: number;
90
+ /** Last version SemVer */
91
+ lastVersion: number;
92
+ hasChangedAuthor: boolean;
93
+ hasManyPublishers: boolean;
94
+ hasReceivedUpdateInOneYear: boolean;
95
+ /** Author of the package. This information is not trustable and can be empty. */
96
+ author: Maintainer;
97
+ /** Package home page */
98
+ homepage: string | null;
99
+ /**
100
+ * List of maintainers (list of people in the organization related to the package)
101
+ */
102
+ maintainers: { name: string, email: string }[];
103
+ /**
104
+ * List of people who published this package
105
+ */
106
+ publishers: Publisher[];
107
+ }
108
+ /** List of versions of this package available in the dependency tree (In the payload) */
109
+ versions: Record<string, DependencyVersion>;
110
+ /**
111
+ * Vulnerabilities fetched dependending on the selected vulnerabilityStrategy
112
+ *
113
+ * @see https://github.com/NodeSecure/vuln
114
+ */
115
+ vulnerabilities: Vuln.Strategy.StandardVulnerability[];
116
+ }
117
+
118
+ export type GlobalWarning = string[];
119
+ export type Dependencies = Record<string, Dependency>;
120
+
121
+ export interface Payload {
122
+ /** Payload unique id */
123
+ id: string;
124
+ /** Name of the analyzed package */
125
+ rootDependencyName: string;
126
+ /** Global warnings list */
127
+ warnings: GlobalWarning[];
128
+ /** All the dependencies of the package (flattened) */
129
+ dependencies: Dependencies;
130
+ /** Version of the scanner used to generate the result */
131
+ scannerVersion: string;
132
+ /** Vulnerability strategy name (npm, snyk, node) */
133
+ vulnerabilityStrategy: Vuln.Strategy.Kind;
134
+ }
135
+
136
+ export interface VerifyPayload {
137
+ files: {
138
+ list: string[];
139
+ extensions: string[];
140
+ minified: string[];
141
+ };
142
+ directorySize: number;
143
+ uniqueLicenseIds: string[];
144
+ licenses: License[];
145
+ ast: {
146
+ dependencies: Record<string, JSXRay.Dependency>;
147
+ warnings: JSXRay.Warning<JSXRay.BaseWarning>[];
148
+ };
149
+ }
150
+
151
+ export interface Options {
152
+ /**
153
+ * Maximum tree depth
154
+ *
155
+ * @default 4
156
+ */
157
+ readonly maxDepth?: number;
158
+ /**
159
+ * Use root package-lock.json. This will have the effect of triggering the Arborist package.
160
+ *
161
+ * @default false for from() API
162
+ * @default true for cwd() API
163
+ */
164
+ readonly usePackageLock?: boolean;
165
+ /**
166
+ * Vulnerability strategy name (npm, snyk, node)
167
+ *
168
+ * @default NONE
169
+ */
170
+ readonly vulnerabilityStrategy: Vuln.Strategy.Kind;
171
+ /**
172
+ * Analyze root package.
173
+ *
174
+ * @default false for from() API
175
+ * @default true for cwd() API
176
+ */
177
+ readonly forceRootAnalysis?: boolean;
178
+ /**
179
+ * Deeper dependencies analysis with cwd() API.
180
+ *
181
+ * @default false
182
+ */
183
+ readonly fullLockMode?: boolean;
184
+ }
185
+ }