node-power-user 2.1.5 → 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.
@@ -1,18 +1,42 @@
1
1
  // Libraries
2
2
  const logger = new (require('../lib/logger'))('node-power-user');
3
- const socket = require('../lib/socket');
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
- // Check socket status upfront (blocks if not installed unless --force)
8
- await socket.check({ force: options.force });
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
- // Run audit
11
- logger.log('Running Socket audit on current dependency tree...');
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 socket.audit({ force: options.force });
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.error(e.message);
40
+ logger.warn('npm audit found vulnerabilities. Review the output above.');
17
41
  }
18
42
  };
@@ -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 socket
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('To retry with Socket protection bypassed:');
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
  };
@@ -103,7 +103,7 @@ module.exports = async function (options) {
103
103
  latestVersion,
104
104
  type: getDependencyType(projectJson, dep),
105
105
  hasDiscrepancy,
106
- hasMajorUpdate: latestVersion && minorVersion !== latestVersion,
106
+ hasMajorUpdate: latestVersion && latestVersion.split('.')[0] !== packageVersion.split('.')[0],
107
107
  });
108
108
  }
109
109
  }
@@ -186,9 +186,11 @@ module.exports = async function (options) {
186
186
  );
187
187
 
188
188
  // Check if major/latest offers any versions beyond what minor gives
189
- const hasMajorBeyondMinor = Object.keys(latestUpgrades).some(dep =>
190
- latestUpgrades[dep] !== (minorUpgrades[dep] || allDependencies[dep])
191
- );
189
+ const hasMajorBeyondMinor = Object.keys(latestUpgrades).some(dep => {
190
+ const latestMajor = version.clean(latestUpgrades[dep]).split('.')[0];
191
+ const currentMajor = version.clean(allDependencies[dep]).split('.')[0];
192
+ return latestMajor !== currentMajor;
193
+ });
192
194
 
193
195
  // If noPrompt, return data without showing menu (used by tests)
194
196
  if (options.noPrompt) {
@@ -454,65 +456,16 @@ module.exports = async function (options) {
454
456
  logger.log('package.json and package-lock.json have been restored to their original state.');
455
457
  logger.log('The removed package copies will show as missing in the next check until reinstalled.');
456
458
 
457
- // npm itself failed (ERESOLVE, network, peer-dep conflict) — not a Socket block.
458
- // The npm error was already printed above; just acknowledge and stop.
459
459
  if (e.reason === 'npm-failed') {
460
460
  logger.log('');
461
461
  logger.log('Fix the npm error above (e.g. resolve peer-dep conflicts) and retry.');
462
462
  return { allPackages, desynced, updated: false, target: action };
463
463
  }
464
464
 
465
- const flaggedPackages = e.flaggedPackages || [];
466
-
467
- // Trace which of the requested packages bring in the flagged deps
468
- const riskyParents = new Set();
469
-
470
- if (flaggedPackages.length > 0) {
471
- logger.log('');
472
- logger.error('Socket flagged the following transitive dependencies:');
473
-
474
- for (const flagged of flaggedPackages) {
475
- const flaggedName = flagged.replace(/@[^@]+$/, ''); // strip version
476
- let parentChain = '';
477
-
478
- try {
479
- const lsOutput = await execute(`npm ls ${flaggedName} --json`, { log: false });
480
- const tree = JSON.parse(lsOutput);
481
-
482
- // Find which top-level deps depend on the flagged package
483
- const parents = [];
484
- for (const [dep, info] of Object.entries(tree.dependencies || {})) {
485
- if (dep === flaggedName || JSON.stringify(info).includes(`"${flaggedName}"`)) {
486
- parents.push(dep);
487
- riskyParents.add(dep);
488
- }
489
- }
490
-
491
- if (parents.length > 0) {
492
- parentChain = chalk.dim(` (from ${parents.join(', ')})`);
493
- }
494
- } catch (_) {
495
- // npm ls may fail, just skip the trace
496
- }
497
-
498
- logger.error(` • ${flagged}${parentChain}`);
499
- }
500
- }
501
-
502
- // Suggest retry commands
503
- if (riskyParents.size > 0) {
504
- const ignoreArg = [...riskyParents].join(',');
505
- logger.log('');
506
- logger.log('To skip the risky packages and update the rest:');
507
- logger.log(logger.format.cyan(` npu out --ignore ${ignoreArg}`));
508
- }
509
-
510
465
  logger.log('');
511
- logger.log('To retry with Socket protection bypassed:');
466
+ logger.log('Install blocked. See the output above for details.');
467
+ logger.log('To retry without firewall protection:');
512
468
  logger.log(logger.format.cyan(` npu out --force`));
513
- logger.log('');
514
- logger.log('To bypass Socket for this install only:');
515
- logger.log(logger.format.cyan(` SOCKET_CLI_ACCEPT_RISKS=1 npm install ${packageNames.map(name => `${name}@${version.clean(upgrades[name])}`).join(' ')}`));
516
469
 
517
470
  return { allPackages, desynced, updated: false, target: action };
518
471
  }
@@ -533,28 +486,11 @@ module.exports = async function (options) {
533
486
  return { allPackages, desynced, updated: true, target: action };
534
487
  };
535
488
 
536
- // Helper to explain a Socket risk-block after stale copies were already removed.
537
- // Deliberately does NOT reinstall the removed copies — that would silently
538
- // bypass the block Socket just raised; removed is safer than stale-and-risky.
539
489
  function reportSocketBlock(e, retryCommand) {
540
- const flaggedPackages = e.flaggedPackages || [];
541
-
542
- if (flaggedPackages.length > 0) {
543
- logger.log('');
544
- logger.error('Socket flagged the following dependencies:');
545
- flaggedPackages.forEach(pkg => logger.error(` • ${pkg}`));
546
-
547
- const flaggedNames = flaggedPackages.map(pkg => pkg.replace(/@[^@]+$/, ''));
548
- logger.log('');
549
- logger.log('If these are fixable CVEs in transitive deps pinned by package-lock.json, re-resolve them with:');
550
- logger.log(logger.format.cyan(` socket npm update ${flaggedNames.join(' ')}`));
551
- }
552
-
553
490
  logger.log('');
554
- logger.log('To retry with Socket protection bypassed:');
491
+ logger.log('Install blocked. See the output above for details.');
492
+ logger.log('To retry without firewall protection:');
555
493
  logger.log(logger.format.cyan(` ${retryCommand}`));
556
- logger.log('');
557
- logger.log('The risky copies stay removed from node_modules (safer than leaving stale versions) and will show as missing until reinstalled.');
558
494
  }
559
495
 
560
496
  // Helper to report packages that npm claimed to install but didn't physically land.
@@ -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 socket CLI is installed
6
- let _socketVersion = undefined;
7
- async function isSocketAvailable() {
8
- if (_socketVersion !== undefined) {
9
- return !!_socketVersion;
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
- _socketVersion = (await execute('socket --version', { log: false })).trim();
13
+ _sfwVersion = (await execute('sfw --version', { log: false })).trim();
14
14
  } catch (e) {
15
- _socketVersion = null;
15
+ _sfwVersion = null;
16
16
  }
17
17
 
18
- return !!_socketVersion;
18
+ return !!_sfwVersion;
19
19
  }
20
20
 
21
- // Log socket status upfront (call at the start of commands)
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 isSocketAvailable();
25
+ const available = await isFirewallAvailable();
26
26
 
27
27
  if (available) {
28
- logger.log(logger.format.green(`Socket v${_socketVersion} — supply chain protection enabled.`));
28
+ logger.log(logger.format.green(`${_sfwVersion} — supply chain protection enabled.`));
29
29
  } else {
30
- logger.error('Socket CLI is not installed. Your installs are NOT protected against supply chain attacks.');
31
- logger.error(`Install it with: ${logger.format.cyan('npm install -g @socketsecurity/cli --save-exact')}`);
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 Socket protection. Use --force to bypass.');
35
- throw new Error('Socket CLI is not installed.');
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 Socket protection (--force flag detected).');
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 socket if available
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 isSocketAvailable();
48
+ const available = await isFirewallAvailable();
49
49
 
50
- // If socket is not installed, fall back to plain npm
51
- if (!available) {
52
- await execute(command, { log: true });
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 socket and capture output to check for risks
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(`${env}socket ${command}`, { log: false });
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
- // Distinguish a real Socket risk-block from a generic npm failure.
76
- // Socket prints its own markers when it blocks; npm failures (ERESOLVE,
77
- // network errors, peer-dep conflicts) just exit non-zero with npm errors.
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
- // Run a full socket audit on the current project
116
- async function audit(options) {
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: isSocketAvailable,
89
+ isAvailable: isFirewallAvailable,
170
90
  check,
171
91
  wrap,
172
92
  audit,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-power-user",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
4
4
  "description": "Easy tools for every Node.js developer!",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {