forked from NodeSecure/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
112 lines (90 loc) · 3.71 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
"use strict";
// Require Node.js Dependencies
const os = require("os");
const { join, extname } = require("path");
const { readFile, rmdir } = require("fs").promises;
const { promisify } = require("util");
// Require Third-party Dependencies
const pacote = require("pacote");
const uniqueSlug = require("unique-slug");
const { searchRuntimeDependencies } = require("js-x-ray");
const ntlp = require("ntlp");
const isMinified = require("is-minified-code");
// Require Internal Dependencies
const { depWalker } = require("./src/depWalker");
const { getRegistryURL, getTarballComposition } = require("./src/utils");
// CONSTANTS
const TMP = os.tmpdir();
const REGISTRY_DEFAULT_ADDR = getRegistryURL();
const JS_EXTENSIONS = new Set([".js", ".mjs"]);
// VARS
const nextTick = promisify(setImmediate);
async function cwd(cwd = process.cwd(), options) {
const packagePath = join(cwd, "package.json");
const str = await readFile(packagePath, "utf-8");
return depWalker(JSON.parse(str), options);
}
async function from(packageName, options) {
const token = typeof process.env.NODE_SECURE_TOKEN === "string" ? { token: process.env.NODE_SECURE_TOKEN } : {};
const manifest = await pacote.manifest(packageName, token);
return depWalker(manifest, options);
}
async function readJSFile(dest, file) {
const str = await readFile(join(dest, file), "utf-8");
return [file, str];
}
async function verify(packageName) {
const token = typeof process.env.NODE_SECURE_TOKEN === "string" ? { token: process.env.NODE_SECURE_TOKEN } : {};
const tmpLocation = join(TMP, uniqueSlug());
const dest = join(tmpLocation, packageName);
try {
await pacote.extract(packageName, dest, {
...token, registry: REGISTRY_DEFAULT_ADDR, cache: `${process.env.HOME}/.npm`
});
// Read the package.json file inside the extracted directory.
let isProjectUsingESM = false;
{
const packageStr = await readFile(join(dest, "package.json"), "utf-8");
const { type = "script" } = JSON.parse(packageStr);
isProjectUsingESM = type === "module";
}
// Get the tarball composition
await nextTick();
const { ext, files, size } = await getTarballComposition(dest);
// Search for runtime dependencies
const dependencies = Object.create(null);
const minified = [];
const warnings = [];
const JSFiles = files.filter((name) => JS_EXTENSIONS.has(extname(name)));
const allFilesContent = (await Promise.allSettled(JSFiles.map((file) => readJSFile(dest, file))))
.filter((_p) => _p.status === "fulfilled").map((_p) => _p.value);
// TODO: 2) handle dependency by file to not loose data.
for (const [file, str] of allFilesContent) {
const ASTAnalysis = searchRuntimeDependencies(str, {
module: extname(file) === ".mjs" ? true : isProjectUsingESM
});
ASTAnalysis.dependencies.removeByName(packageName);
dependencies[file] = ASTAnalysis.dependencies.dependencies;
warnings.push(...ASTAnalysis.warnings);
if (!ASTAnalysis.isOneLineRequire && !file.includes(".min") && isMinified(str)) {
minified.push(file);
}
}
await nextTick();
const { uniqueLicenseIds, licenses } = await ntlp(dest);
return {
files: { list: files, extensions: [...ext], minified },
directorySize: size,
uniqueLicenseIds,
licenses,
ast: { dependencies, warnings }
};
}
finally {
await nextTick();
await rmdir(tmpLocation, { recursive: true });
}
}
module.exports = {
cwd, from, verify
};