@guardinstall/cli 0.1.3 → 0.1.4
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/dist/index.js +1 -1
- package/dist/resolver.js +81 -18
- package/package.json +3 -3
- package/src/index.ts +1 -1
- package/src/resolver.ts +80 -19
package/dist/index.js
CHANGED
|
@@ -39,7 +39,7 @@ const program = new commander_1.Command();
|
|
|
39
39
|
program
|
|
40
40
|
.name('guardinstall')
|
|
41
41
|
.description('A kernel-level behavioral sandbox for npm/pnpm/bun install scripts')
|
|
42
|
-
.version('0.
|
|
42
|
+
.version('0.1.3');
|
|
43
43
|
program
|
|
44
44
|
.command('install')
|
|
45
45
|
.description('Run npm install with sandbox protection')
|
package/dist/resolver.js
CHANGED
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getInstallScripts = void 0;
|
|
7
7
|
const arborist_1 = __importDefault(require("@npmcli/arborist"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
10
|
const lockfile_1 = require("./lockfile");
|
|
9
11
|
async function getInstallScripts(projectRoot) {
|
|
10
12
|
const arb = new arborist_1.default({ path: projectRoot });
|
|
@@ -12,32 +14,93 @@ async function getInstallScripts(projectRoot) {
|
|
|
12
14
|
try {
|
|
13
15
|
tree = await arb.loadActual();
|
|
14
16
|
}
|
|
15
|
-
catch {
|
|
16
|
-
tree
|
|
17
|
+
catch (e) {
|
|
18
|
+
console.error('Warning: Error loading package tree:', e instanceof Error ? e.message : e);
|
|
19
|
+
return getInstallScriptsFromNodeModules(projectRoot); // Fallback to manual scan
|
|
17
20
|
}
|
|
18
21
|
const packagesWithScripts = [];
|
|
19
22
|
const lockfile = (0, lockfile_1.readLockfile)(projectRoot);
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
try {
|
|
24
|
+
for (const node of tree.inventory.values()) {
|
|
25
|
+
const pkg = node.package;
|
|
26
|
+
if (!pkg)
|
|
27
|
+
continue;
|
|
28
|
+
const scripts = pkg.scripts ?? {};
|
|
29
|
+
const hasInstallScript = ['preinstall', 'install', 'postinstall']
|
|
30
|
+
.some(s => s in scripts);
|
|
31
|
+
if (hasInstallScript) {
|
|
32
|
+
const installScripts = pick(scripts, ['preinstall', 'install', 'postinstall']);
|
|
33
|
+
packagesWithScripts.push({
|
|
34
|
+
name: node.name,
|
|
35
|
+
version: node.version,
|
|
36
|
+
scripts: installScripts,
|
|
37
|
+
path: node.path,
|
|
38
|
+
isNew: !(0, lockfile_1.isInLockfile)(node.name, node.version, lockfile)
|
|
39
|
+
});
|
|
40
|
+
}
|
|
36
41
|
}
|
|
37
42
|
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
console.error('Warning: Error iterating package tree:', e instanceof Error ? e.message : e);
|
|
45
|
+
return getInstallScriptsFromNodeModules(projectRoot);
|
|
46
|
+
}
|
|
38
47
|
return packagesWithScripts;
|
|
39
48
|
}
|
|
40
49
|
exports.getInstallScripts = getInstallScripts;
|
|
50
|
+
// Fallback: manually scan node_modules for package.json files
|
|
51
|
+
function getInstallScriptsFromNodeModules(projectRoot) {
|
|
52
|
+
const packagesWithScripts = [];
|
|
53
|
+
const lockfile = (0, lockfile_1.readLockfile)(projectRoot);
|
|
54
|
+
try {
|
|
55
|
+
const nodeModulesPath = path_1.default.join(projectRoot, 'node_modules');
|
|
56
|
+
if (!fs_1.default.existsSync(nodeModulesPath))
|
|
57
|
+
return packagesWithScripts;
|
|
58
|
+
const scanDir = (dir, depth = 0) => {
|
|
59
|
+
if (depth > 2)
|
|
60
|
+
return; // Limit recursion depth
|
|
61
|
+
try {
|
|
62
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
if (entry.name.startsWith('@')) {
|
|
66
|
+
// Scoped package directory
|
|
67
|
+
scanDir(path_1.default.join(dir, entry.name), depth + 1);
|
|
68
|
+
}
|
|
69
|
+
else if (depth === 0 || depth === 1) {
|
|
70
|
+
// Check package.json in this directory
|
|
71
|
+
const pkgPath = path_1.default.join(dir, entry.name, 'package.json');
|
|
72
|
+
if (fs_1.default.existsSync(pkgPath)) {
|
|
73
|
+
try {
|
|
74
|
+
const pkgJson = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
|
|
75
|
+
if (pkgJson.scripts && ('preinstall' in pkgJson.scripts || 'install' in pkgJson.scripts || 'postinstall' in pkgJson.scripts)) {
|
|
76
|
+
packagesWithScripts.push({
|
|
77
|
+
name: pkgJson.name || entry.name,
|
|
78
|
+
version: pkgJson.version || 'unknown',
|
|
79
|
+
scripts: pick(pkgJson.scripts, ['preinstall', 'install', 'postinstall']),
|
|
80
|
+
path: path_1.default.join(dir, entry.name),
|
|
81
|
+
isNew: !(0, lockfile_1.isInLockfile)(pkgJson.name || entry.name, pkgJson.version || 'unknown', lockfile)
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
// Ignore parse errors
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
// Ignore permission errors
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
scanDir(nodeModulesPath);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
// Ignore errors
|
|
101
|
+
}
|
|
102
|
+
return packagesWithScripts;
|
|
103
|
+
}
|
|
41
104
|
function pick(obj, keys) {
|
|
42
105
|
const result = {};
|
|
43
106
|
for (const key of keys) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guardinstall/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "CLI wrapper for guardinstall - intercepts and sandboxes install scripts",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"test": "jest"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@guardinstall/policy-engine": "0.1.
|
|
17
|
-
"@guardinstall/sandbox": "0.1.
|
|
16
|
+
"@guardinstall/policy-engine": "0.1.4",
|
|
17
|
+
"@guardinstall/sandbox": "0.1.4",
|
|
18
18
|
"@npmcli/arborist": "7.5.4",
|
|
19
19
|
"chalk": "4.1.2",
|
|
20
20
|
"commander": "12.1.0"
|
package/src/index.ts
CHANGED
package/src/resolver.ts
CHANGED
|
@@ -17,36 +17,97 @@ export async function getInstallScripts(projectRoot: string): Promise<PackageInf
|
|
|
17
17
|
let tree
|
|
18
18
|
try {
|
|
19
19
|
tree = await arb.loadActual()
|
|
20
|
-
} catch {
|
|
21
|
-
tree
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error('Warning: Error loading package tree:', e instanceof Error ? e.message : e)
|
|
22
|
+
return getInstallScriptsFromNodeModules(projectRoot) // Fallback to manual scan
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const packagesWithScripts: PackageInfo[] = []
|
|
25
26
|
const lockfile = readLockfile(projectRoot)
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
try {
|
|
29
|
+
for (const node of tree.inventory.values()) {
|
|
30
|
+
const pkg = node.package
|
|
31
|
+
if (!pkg) continue
|
|
32
|
+
|
|
33
|
+
const scripts = pkg.scripts ?? {}
|
|
34
|
+
const hasInstallScript = ['preinstall', 'install', 'postinstall']
|
|
35
|
+
.some(s => s in scripts)
|
|
36
|
+
|
|
37
|
+
if (hasInstallScript) {
|
|
38
|
+
const installScripts = pick(scripts, ['preinstall', 'install', 'postinstall'])
|
|
39
|
+
packagesWithScripts.push({
|
|
40
|
+
name: node.name,
|
|
41
|
+
version: node.version,
|
|
42
|
+
scripts: installScripts as Record<string, string> | undefined,
|
|
43
|
+
path: node.path,
|
|
44
|
+
isNew: !isInLockfile(node.name, node.version, lockfile)
|
|
45
|
+
})
|
|
46
|
+
}
|
|
44
47
|
}
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.error('Warning: Error iterating package tree:', e instanceof Error ? e.message : e)
|
|
50
|
+
return getInstallScriptsFromNodeModules(projectRoot)
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
return packagesWithScripts
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
// Fallback: manually scan node_modules for package.json files
|
|
57
|
+
function getInstallScriptsFromNodeModules(projectRoot: string): PackageInfo[] {
|
|
58
|
+
const packagesWithScripts: PackageInfo[] = []
|
|
59
|
+
const lockfile = readLockfile(projectRoot)
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const nodeModulesPath = path.join(projectRoot, 'node_modules')
|
|
63
|
+
if (!fs.existsSync(nodeModulesPath)) return packagesWithScripts
|
|
64
|
+
|
|
65
|
+
const scanDir = (dir: string, depth: number = 0) => {
|
|
66
|
+
if (depth > 2) return // Limit recursion depth
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
70
|
+
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
if (entry.isDirectory()) {
|
|
73
|
+
if (entry.name.startsWith('@')) {
|
|
74
|
+
// Scoped package directory
|
|
75
|
+
scanDir(path.join(dir, entry.name), depth + 1)
|
|
76
|
+
} else if (depth === 0 || depth === 1) {
|
|
77
|
+
// Check package.json in this directory
|
|
78
|
+
const pkgPath = path.join(dir, entry.name, 'package.json')
|
|
79
|
+
if (fs.existsSync(pkgPath)) {
|
|
80
|
+
try {
|
|
81
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
|
|
82
|
+
if (pkgJson.scripts && ('preinstall' in pkgJson.scripts || 'install' in pkgJson.scripts || 'postinstall' in pkgJson.scripts)) {
|
|
83
|
+
packagesWithScripts.push({
|
|
84
|
+
name: pkgJson.name || entry.name,
|
|
85
|
+
version: pkgJson.version || 'unknown',
|
|
86
|
+
scripts: pick(pkgJson.scripts, ['preinstall', 'install', 'postinstall']) as Record<string, string> | undefined,
|
|
87
|
+
path: path.join(dir, entry.name),
|
|
88
|
+
isNew: !isInLockfile(pkgJson.name || entry.name, pkgJson.version || 'unknown', lockfile)
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
} catch (e) {
|
|
92
|
+
// Ignore parse errors
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// Ignore permission errors
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
scanDir(nodeModulesPath)
|
|
104
|
+
} catch (e) {
|
|
105
|
+
// Ignore errors
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return packagesWithScripts
|
|
109
|
+
}
|
|
110
|
+
|
|
50
111
|
function pick<T extends Record<string, unknown>>(obj: T, keys: string[]): Partial<T> {
|
|
51
112
|
const result: Partial<T> = {}
|
|
52
113
|
for (const key of keys) {
|