node-power-user 2.1.6 → 2.1.7
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/commands/audit.js +31 -7
- package/dist/commands/install.js +3 -19
- package/dist/commands/outdated.js +4 -70
- package/dist/lib/socket.js +35 -115
- package/package.json +1 -1
package/dist/commands/audit.js
CHANGED
|
@@ -1,18 +1,42 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
const logger = new (require('../lib/logger'))('node-power-user');
|
|
3
|
-
const
|
|
3
|
+
const { execute } = require('node-powertools');
|
|
4
|
+
|
|
5
|
+
let _hasSocketCli;
|
|
6
|
+
async function isSocketCliAvailable() {
|
|
7
|
+
if (_hasSocketCli !== undefined) {
|
|
8
|
+
return _hasSocketCli;
|
|
9
|
+
}
|
|
10
|
+
_hasSocketCli = await execute('socket --version', { log: false }).then(() => true).catch(() => false);
|
|
11
|
+
return _hasSocketCli;
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
// Module
|
|
6
15
|
module.exports = async function (options) {
|
|
7
|
-
|
|
8
|
-
|
|
16
|
+
const hasSocket = await isSocketCliAvailable();
|
|
17
|
+
|
|
18
|
+
if (hasSocket) {
|
|
19
|
+
logger.log('Running Socket audit on current dependency tree...');
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
await execute('socket npm audit', { log: true });
|
|
23
|
+
logger.log(logger.format.green('Socket audit passed — no risks detected.'));
|
|
24
|
+
} catch (e) {
|
|
25
|
+
logger.warn('Socket audit found risks. Review the output above.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
9
30
|
|
|
10
|
-
//
|
|
11
|
-
logger.log('
|
|
31
|
+
// Fall back to npm audit
|
|
32
|
+
logger.log('Socket CLI not installed — falling back to npm audit...');
|
|
33
|
+
logger.log(logger.format.cyan(`Install Socket CLI for deeper analysis: npm install -g @socketsecurity/cli`));
|
|
34
|
+
logger.log('');
|
|
12
35
|
|
|
13
36
|
try {
|
|
14
|
-
await
|
|
37
|
+
await execute('npm audit', { log: true });
|
|
38
|
+
logger.log(logger.format.green('npm audit passed — no known vulnerabilities.'));
|
|
15
39
|
} catch (e) {
|
|
16
|
-
logger.
|
|
40
|
+
logger.warn('npm audit found vulnerabilities. Review the output above.');
|
|
17
41
|
}
|
|
18
42
|
};
|
package/dist/commands/install.js
CHANGED
|
@@ -53,39 +53,23 @@ module.exports = async function (options) {
|
|
|
53
53
|
// Log
|
|
54
54
|
logger.log(`Running: ${logger.format.cyan(command)}`);
|
|
55
55
|
|
|
56
|
-
// Wrap with
|
|
56
|
+
// Wrap with Socket Firewall
|
|
57
57
|
try {
|
|
58
58
|
await socket.wrap(command, { force: options.force });
|
|
59
59
|
} catch (e) {
|
|
60
|
-
// npm itself failed (ERESOLVE, network, peer-dep conflict) — not a Socket block.
|
|
61
|
-
// The npm error was already printed above; just acknowledge and stop.
|
|
62
60
|
if (e.reason === 'npm-failed') {
|
|
63
61
|
logger.log('');
|
|
64
62
|
logger.log('Fix the npm error above (e.g. resolve peer-dep conflicts) and retry.');
|
|
65
63
|
return;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
const flaggedPackages = e.flaggedPackages || [];
|
|
69
|
-
|
|
70
|
-
if (flaggedPackages.length > 0) {
|
|
71
|
-
logger.log('');
|
|
72
|
-
logger.error('Socket flagged the following dependencies:');
|
|
73
|
-
flaggedPackages.forEach(pkg => logger.error(` • ${pkg}`));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
66
|
logger.log('');
|
|
77
|
-
logger.log('
|
|
67
|
+
logger.log('Install blocked. See the output above for details.');
|
|
68
|
+
logger.log('To retry without firewall protection:');
|
|
78
69
|
logger.log(logger.format.cyan(` npu i ${packages.join(' ')} ${flags.join(' ')} --force`.trim()));
|
|
79
70
|
return;
|
|
80
71
|
}
|
|
81
72
|
|
|
82
|
-
// Run full audit after install
|
|
83
|
-
try {
|
|
84
|
-
await socket.audit({ force: options.force });
|
|
85
|
-
} catch (e) {
|
|
86
|
-
logger.error(`Audit warning: ${e.message}`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
73
|
// Done
|
|
90
74
|
logger.log(logger.format.green('Install complete.'));
|
|
91
75
|
};
|
|
@@ -456,65 +456,16 @@ module.exports = async function (options) {
|
|
|
456
456
|
logger.log('package.json and package-lock.json have been restored to their original state.');
|
|
457
457
|
logger.log('The removed package copies will show as missing in the next check until reinstalled.');
|
|
458
458
|
|
|
459
|
-
// npm itself failed (ERESOLVE, network, peer-dep conflict) — not a Socket block.
|
|
460
|
-
// The npm error was already printed above; just acknowledge and stop.
|
|
461
459
|
if (e.reason === 'npm-failed') {
|
|
462
460
|
logger.log('');
|
|
463
461
|
logger.log('Fix the npm error above (e.g. resolve peer-dep conflicts) and retry.');
|
|
464
462
|
return { allPackages, desynced, updated: false, target: action };
|
|
465
463
|
}
|
|
466
464
|
|
|
467
|
-
const flaggedPackages = e.flaggedPackages || [];
|
|
468
|
-
|
|
469
|
-
// Trace which of the requested packages bring in the flagged deps
|
|
470
|
-
const riskyParents = new Set();
|
|
471
|
-
|
|
472
|
-
if (flaggedPackages.length > 0) {
|
|
473
|
-
logger.log('');
|
|
474
|
-
logger.error('Socket flagged the following transitive dependencies:');
|
|
475
|
-
|
|
476
|
-
for (const flagged of flaggedPackages) {
|
|
477
|
-
const flaggedName = flagged.replace(/@[^@]+$/, ''); // strip version
|
|
478
|
-
let parentChain = '';
|
|
479
|
-
|
|
480
|
-
try {
|
|
481
|
-
const lsOutput = await execute(`npm ls ${flaggedName} --json`, { log: false });
|
|
482
|
-
const tree = JSON.parse(lsOutput);
|
|
483
|
-
|
|
484
|
-
// Find which top-level deps depend on the flagged package
|
|
485
|
-
const parents = [];
|
|
486
|
-
for (const [dep, info] of Object.entries(tree.dependencies || {})) {
|
|
487
|
-
if (dep === flaggedName || JSON.stringify(info).includes(`"${flaggedName}"`)) {
|
|
488
|
-
parents.push(dep);
|
|
489
|
-
riskyParents.add(dep);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (parents.length > 0) {
|
|
494
|
-
parentChain = chalk.dim(` (from ${parents.join(', ')})`);
|
|
495
|
-
}
|
|
496
|
-
} catch (_) {
|
|
497
|
-
// npm ls may fail, just skip the trace
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
logger.error(` • ${flagged}${parentChain}`);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Suggest retry commands
|
|
505
|
-
if (riskyParents.size > 0) {
|
|
506
|
-
const ignoreArg = [...riskyParents].join(',');
|
|
507
|
-
logger.log('');
|
|
508
|
-
logger.log('To skip the risky packages and update the rest:');
|
|
509
|
-
logger.log(logger.format.cyan(` npu out --ignore ${ignoreArg}`));
|
|
510
|
-
}
|
|
511
|
-
|
|
512
465
|
logger.log('');
|
|
513
|
-
logger.log('
|
|
466
|
+
logger.log('Install blocked. See the output above for details.');
|
|
467
|
+
logger.log('To retry without firewall protection:');
|
|
514
468
|
logger.log(logger.format.cyan(` npu out --force`));
|
|
515
|
-
logger.log('');
|
|
516
|
-
logger.log('To bypass Socket for this install only:');
|
|
517
|
-
logger.log(logger.format.cyan(` SOCKET_CLI_ACCEPT_RISKS=1 npm install ${packageNames.map(name => `${name}@${version.clean(upgrades[name])}`).join(' ')}`));
|
|
518
469
|
|
|
519
470
|
return { allPackages, desynced, updated: false, target: action };
|
|
520
471
|
}
|
|
@@ -535,28 +486,11 @@ module.exports = async function (options) {
|
|
|
535
486
|
return { allPackages, desynced, updated: true, target: action };
|
|
536
487
|
};
|
|
537
488
|
|
|
538
|
-
// Helper to explain a Socket risk-block after stale copies were already removed.
|
|
539
|
-
// Deliberately does NOT reinstall the removed copies — that would silently
|
|
540
|
-
// bypass the block Socket just raised; removed is safer than stale-and-risky.
|
|
541
489
|
function reportSocketBlock(e, retryCommand) {
|
|
542
|
-
const flaggedPackages = e.flaggedPackages || [];
|
|
543
|
-
|
|
544
|
-
if (flaggedPackages.length > 0) {
|
|
545
|
-
logger.log('');
|
|
546
|
-
logger.error('Socket flagged the following dependencies:');
|
|
547
|
-
flaggedPackages.forEach(pkg => logger.error(` • ${pkg}`));
|
|
548
|
-
|
|
549
|
-
const flaggedNames = flaggedPackages.map(pkg => pkg.replace(/@[^@]+$/, ''));
|
|
550
|
-
logger.log('');
|
|
551
|
-
logger.log('If these are fixable CVEs in transitive deps pinned by package-lock.json, re-resolve them with:');
|
|
552
|
-
logger.log(logger.format.cyan(` socket npm update ${flaggedNames.join(' ')}`));
|
|
553
|
-
}
|
|
554
|
-
|
|
555
490
|
logger.log('');
|
|
556
|
-
logger.log('
|
|
491
|
+
logger.log('Install blocked. See the output above for details.');
|
|
492
|
+
logger.log('To retry without firewall protection:');
|
|
557
493
|
logger.log(logger.format.cyan(` ${retryCommand}`));
|
|
558
|
-
logger.log('');
|
|
559
|
-
logger.log('The risky copies stay removed from node_modules (safer than leaving stale versions) and will show as missing until reinstalled.');
|
|
560
494
|
}
|
|
561
495
|
|
|
562
496
|
// Helper to report packages that npm claimed to install but didn't physically land.
|
package/dist/lib/socket.js
CHANGED
|
@@ -2,171 +2,91 @@
|
|
|
2
2
|
const { execute } = require('node-powertools');
|
|
3
3
|
const logger = new (require('./logger'))('node-power-user');
|
|
4
4
|
|
|
5
|
-
// Check if
|
|
6
|
-
let
|
|
7
|
-
async function
|
|
8
|
-
if (
|
|
9
|
-
return !!
|
|
5
|
+
// Check if sfw (Socket Firewall) is installed
|
|
6
|
+
let _sfwVersion = undefined;
|
|
7
|
+
async function isFirewallAvailable() {
|
|
8
|
+
if (_sfwVersion !== undefined) {
|
|
9
|
+
return !!_sfwVersion;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
try {
|
|
13
|
-
|
|
13
|
+
_sfwVersion = (await execute('sfw --version', { log: false })).trim();
|
|
14
14
|
} catch (e) {
|
|
15
|
-
|
|
15
|
+
_sfwVersion = null;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
return !!
|
|
18
|
+
return !!_sfwVersion;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// Log
|
|
21
|
+
// Log firewall status upfront (call at the start of commands)
|
|
22
22
|
async function check(options) {
|
|
23
23
|
options = options || {};
|
|
24
24
|
|
|
25
|
-
const available = await
|
|
25
|
+
const available = await isFirewallAvailable();
|
|
26
26
|
|
|
27
27
|
if (available) {
|
|
28
|
-
logger.log(logger.format.green(
|
|
28
|
+
logger.log(logger.format.green(`${_sfwVersion} — supply chain protection enabled.`));
|
|
29
29
|
} else {
|
|
30
|
-
logger.error('Socket
|
|
31
|
-
logger.error(`Install it with: ${logger.format.cyan('npm install -g
|
|
30
|
+
logger.error('Socket Firewall (sfw) is not installed. Installs are NOT protected against supply chain attacks.');
|
|
31
|
+
logger.error(`Install it with: ${logger.format.cyan('npm install -g sfw')}`);
|
|
32
32
|
|
|
33
33
|
if (!options.force) {
|
|
34
|
-
logger.error('Refusing to proceed without
|
|
35
|
-
throw new Error('Socket
|
|
34
|
+
logger.error('Refusing to proceed without firewall protection. Use --force to bypass.');
|
|
35
|
+
throw new Error('Socket Firewall is not installed.');
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
logger.warn('Bypassing
|
|
38
|
+
logger.warn('Bypassing firewall protection (--force flag detected).');
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
return available;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// Wrap an npm command with
|
|
44
|
+
// Wrap an npm command with sfw if available
|
|
45
45
|
async function wrap(command, options) {
|
|
46
46
|
options = options || {};
|
|
47
47
|
|
|
48
|
-
const available = await
|
|
48
|
+
const available = await isFirewallAvailable();
|
|
49
49
|
|
|
50
|
-
// If
|
|
51
|
-
if (!available) {
|
|
52
|
-
|
|
50
|
+
// If firewall is not installed or --force is used, run plain npm
|
|
51
|
+
if (!available || options.force) {
|
|
52
|
+
try {
|
|
53
|
+
await execute(command, { log: true });
|
|
54
|
+
} catch (e) {
|
|
55
|
+
const err = new Error('npm install failed. See the error output above.');
|
|
56
|
+
err.reason = 'npm-failed';
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
53
59
|
return;
|
|
54
60
|
}
|
|
55
61
|
|
|
56
|
-
// Run with
|
|
57
|
-
// When --force is used, set SOCKET_CLI_ACCEPT_RISKS so Socket actually installs
|
|
58
|
-
const env = options.force ? 'SOCKET_CLI_ACCEPT_RISKS=1 ' : '';
|
|
62
|
+
// Run with Socket Firewall
|
|
59
63
|
let output;
|
|
60
64
|
let exitedWithError = false;
|
|
61
65
|
|
|
62
66
|
try {
|
|
63
|
-
output = await execute(
|
|
67
|
+
output = await execute(`sfw ${command}`, { log: false });
|
|
64
68
|
} catch (e) {
|
|
65
|
-
// Socket exits non-zero when it detects risks
|
|
66
69
|
output = e.message || '';
|
|
67
70
|
exitedWithError = true;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
// Print the output
|
|
71
73
|
if (output) {
|
|
72
74
|
console.log(output);
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const socketBlocked = /new risk|socket found|exiting due to risks/i.test(output)
|
|
79
|
-
&& !/no new risks/i.test(output);
|
|
80
|
-
|
|
81
|
-
// Subprocess failed but Socket didn't actually block — surface the npm error honestly.
|
|
82
|
-
if (exitedWithError && !socketBlocked) {
|
|
83
|
-
logger.error('npm install failed. See the error output above.');
|
|
84
|
-
const err = new Error('npm install failed.');
|
|
85
|
-
err.reason = 'npm-failed';
|
|
77
|
+
if (exitedWithError) {
|
|
78
|
+
const err = new Error('Install blocked — Socket Firewall may have flagged a risky package.');
|
|
79
|
+
err.reason = 'sfw-blocked';
|
|
86
80
|
throw err;
|
|
87
81
|
}
|
|
88
|
-
|
|
89
|
-
if (!socketBlocked) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Parse flagged package names from Socket output (e.g. "serialize-javascript@6.0.2")
|
|
94
|
-
const flaggedPackages = [];
|
|
95
|
-
const flaggedRegex = /^(\S+@\S+)\s/gm;
|
|
96
|
-
let match;
|
|
97
|
-
while ((match = flaggedRegex.exec(output)) !== null) {
|
|
98
|
-
flaggedPackages.push(match[1]);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Risks detected — block unless --force flag is passed
|
|
102
|
-
logger.error('Socket detected supply chain risks in the packages above.');
|
|
103
|
-
|
|
104
|
-
if (!options.force) {
|
|
105
|
-
logger.error('Refusing to install. Review the risks above, then use --force to bypass.');
|
|
106
|
-
const err = new Error('Socket detected supply chain risks.');
|
|
107
|
-
err.reason = 'socket-blocked';
|
|
108
|
-
err.flaggedPackages = flaggedPackages;
|
|
109
|
-
throw err;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
logger.warn('Bypassing Socket risk warnings (--force flag detected).');
|
|
113
82
|
}
|
|
114
83
|
|
|
115
|
-
//
|
|
116
|
-
async function audit(
|
|
117
|
-
options = options || {};
|
|
118
|
-
|
|
119
|
-
const available = await isSocketAvailable();
|
|
120
|
-
|
|
121
|
-
if (!available) {
|
|
122
|
-
logger.warn('Skipping post-install audit — Socket CLI is not installed.');
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
logger.log(logger.format.cyan('\nRunning post-install Socket audit...'));
|
|
127
|
-
|
|
128
|
-
let output;
|
|
129
|
-
let exitedWithError = false;
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
output = await execute('socket npm audit', { log: false });
|
|
133
|
-
} catch (e) {
|
|
134
|
-
output = e.message || '';
|
|
135
|
-
exitedWithError = true;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Print the output
|
|
139
|
-
if (output) {
|
|
140
|
-
console.log(output);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Distinguish a real Socket risk-finding from a generic audit-subprocess failure.
|
|
144
|
-
const socketFoundRisks = /new risk|socket found|exiting due to risks/i.test(output)
|
|
145
|
-
&& !/no new risks/i.test(output);
|
|
146
|
-
|
|
147
|
-
if (exitedWithError && !socketFoundRisks) {
|
|
148
|
-
logger.warn('Socket audit subprocess failed (not a risk finding). See output above.');
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (!socketFoundRisks) {
|
|
153
|
-
logger.log(logger.format.green('Socket audit passed — no risks detected.'));
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
logger.error('Socket audit found supply chain risks in your dependencies.');
|
|
158
|
-
|
|
159
|
-
if (!options.force) {
|
|
160
|
-
logger.error('Review the risks above. Use --force to bypass.');
|
|
161
|
-
throw new Error('Socket audit detected supply chain risks.');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
logger.warn('Bypassing Socket audit warnings (--force flag detected).');
|
|
165
|
-
}
|
|
84
|
+
// Socket Firewall handles protection at install time — no separate audit step needed
|
|
85
|
+
async function audit() {}
|
|
166
86
|
|
|
167
87
|
// Export
|
|
168
88
|
module.exports = {
|
|
169
|
-
isAvailable:
|
|
89
|
+
isAvailable: isFirewallAvailable,
|
|
170
90
|
check,
|
|
171
91
|
wrap,
|
|
172
92
|
audit,
|