@socketsecurity/cli 0.14.40 → 0.14.42
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/constants.d.ts +17 -9
- package/dist/constants.d.ts.map +1 -1
- package/dist/module-sync/cli.js +105 -164
- package/dist/module-sync/npm-injection.js +238 -309
- package/dist/module-sync/npm-paths.d.ts +14 -0
- package/dist/module-sync/{path-resolve.js → npm-paths.js} +149 -21
- package/dist/module-sync/path-resolve.d.ts +4 -5
- package/dist/module-sync/settings.d.ts +6 -1
- package/dist/module-sync/shadow-bin.js +9 -13
- package/dist/module-sync/socket-url.d.ts +17 -1
- package/dist/module-sync/socket-url.js +85 -6
- package/dist/require/cli.js +105 -164
- package/dist/require/npm-paths.js +3 -0
- package/dist/require/vendor.js +4 -16
- package/package.json +29 -21
- package/dist/require/path-resolve.js +0 -3
|
@@ -13,78 +13,42 @@ var path = require('node:path');
|
|
|
13
13
|
var process = require('node:process');
|
|
14
14
|
var semver = _socketInterop(require('semver'));
|
|
15
15
|
var registry = require('@socketsecurity/registry');
|
|
16
|
+
var arrays = require('@socketsecurity/registry/lib/arrays');
|
|
16
17
|
var objects = require('@socketsecurity/registry/lib/objects');
|
|
17
18
|
var packages = require('@socketsecurity/registry/lib/packages');
|
|
18
19
|
var prompts = require('@socketsecurity/registry/lib/prompts');
|
|
19
20
|
var spinner = require('@socketsecurity/registry/lib/spinner');
|
|
21
|
+
var constants = require('./constants.js');
|
|
20
22
|
var events = require('node:events');
|
|
21
23
|
var https = require('node:https');
|
|
22
24
|
var readline = require('node:readline');
|
|
23
|
-
var constants = require('./constants.js');
|
|
24
25
|
var socketUrl = require('./socket-url.js');
|
|
25
|
-
var fs = require('node:fs');
|
|
26
26
|
var promises = require('node:timers/promises');
|
|
27
|
-
var
|
|
28
|
-
var pathResolve = require('./path-resolve.js');
|
|
27
|
+
var npmPaths = require('./npm-paths.js');
|
|
29
28
|
var npa = _socketInterop(require('npm-package-arg'));
|
|
30
29
|
|
|
31
30
|
const {
|
|
32
|
-
API_V0_URL,
|
|
33
31
|
LOOP_SENTINEL: LOOP_SENTINEL$2,
|
|
34
|
-
|
|
35
|
-
abortSignal: abortSignal$2
|
|
32
|
+
NPM_REGISTRY_URL: NPM_REGISTRY_URL$1
|
|
36
33
|
} = constants;
|
|
37
|
-
|
|
38
|
-
const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
|
|
39
|
-
method: 'POST',
|
|
40
|
-
headers: {
|
|
41
|
-
Authorization: `Basic ${Buffer.from(`${socketUrl.getPublicToken()}:`).toString('base64url')}`
|
|
42
|
-
},
|
|
43
|
-
signal: abortSignal$2
|
|
44
|
-
}).end(JSON.stringify({
|
|
45
|
-
components: pkgIds.map(id => ({
|
|
46
|
-
purl: `pkg:npm/${id}`
|
|
47
|
-
}))
|
|
48
|
-
}));
|
|
49
|
-
const {
|
|
50
|
-
0: res
|
|
51
|
-
} = await events.once(req, 'response');
|
|
52
|
-
const ok = res.statusCode >= 200 && res.statusCode <= 299;
|
|
53
|
-
if (!ok) {
|
|
54
|
-
throw new Error(`Socket API Error: ${res.statusCode}`);
|
|
55
|
-
}
|
|
56
|
-
const rli = readline.createInterface(res);
|
|
57
|
-
for await (const line of rli) {
|
|
58
|
-
yield JSON.parse(line);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function isAlertFixable(alert) {
|
|
62
|
-
return alert.type === 'socketUpgradeAvailable' || isAlertFixableCve(alert);
|
|
63
|
-
}
|
|
64
|
-
function isAlertFixableCve(alert) {
|
|
65
|
-
const {
|
|
66
|
-
type
|
|
67
|
-
} = alert;
|
|
68
|
-
return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'];
|
|
69
|
-
}
|
|
70
|
-
function toRepoUrl(resolved) {
|
|
34
|
+
function getUrlOrigin(input) {
|
|
71
35
|
try {
|
|
72
|
-
return URL.parse(
|
|
36
|
+
return URL.parse(input)?.origin ?? '';
|
|
73
37
|
} catch {}
|
|
74
38
|
return '';
|
|
75
39
|
}
|
|
76
|
-
function
|
|
40
|
+
function getPackagesToQueryFromDiff(diff_, options) {
|
|
77
41
|
const {
|
|
78
|
-
|
|
79
|
-
|
|
42
|
+
includeUnchanged = false,
|
|
43
|
+
includeUnknownOrigin = false
|
|
80
44
|
} = {
|
|
81
45
|
__proto__: null,
|
|
82
46
|
...options
|
|
83
47
|
};
|
|
84
|
-
const
|
|
48
|
+
const details = [];
|
|
85
49
|
// `diff_` is `null` when `npm install --package-lock-only` is passed.
|
|
86
50
|
if (!diff_) {
|
|
87
|
-
return
|
|
51
|
+
return details;
|
|
88
52
|
}
|
|
89
53
|
const queue = [...diff_.children];
|
|
90
54
|
let pos = 0;
|
|
@@ -114,25 +78,28 @@ function walk(diff_, options) {
|
|
|
114
78
|
if (pkgNode?.package.version !== oldNode?.package.version) {
|
|
115
79
|
keep = true;
|
|
116
80
|
if (oldNode?.package.name && oldNode.package.name === pkgNode?.package.name) {
|
|
117
|
-
existing = oldNode
|
|
81
|
+
existing = oldNode;
|
|
118
82
|
}
|
|
119
83
|
}
|
|
120
84
|
} else {
|
|
121
85
|
keep = action !== 'REMOVE';
|
|
122
86
|
}
|
|
123
87
|
if (keep && pkgNode?.resolved && (!oldNode || oldNode.resolved)) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
88
|
+
const origin = getUrlOrigin(pkgNode.resolved);
|
|
89
|
+
if (includeUnknownOrigin || origin === NPM_REGISTRY_URL$1) {
|
|
90
|
+
details.push({
|
|
91
|
+
node: pkgNode,
|
|
92
|
+
origin,
|
|
93
|
+
existing
|
|
94
|
+
});
|
|
95
|
+
}
|
|
129
96
|
}
|
|
130
97
|
}
|
|
131
98
|
for (const child of diff.children) {
|
|
132
99
|
queue[queueLength++] = child;
|
|
133
100
|
}
|
|
134
101
|
}
|
|
135
|
-
if (
|
|
102
|
+
if (includeUnchanged) {
|
|
136
103
|
const {
|
|
137
104
|
unchanged
|
|
138
105
|
} = diff_;
|
|
@@ -140,14 +107,55 @@ function walk(diff_, options) {
|
|
|
140
107
|
length
|
|
141
108
|
} = unchanged; i < length; i += 1) {
|
|
142
109
|
const pkgNode = unchanged[i];
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
110
|
+
const origin = getUrlOrigin(pkgNode.resolved);
|
|
111
|
+
if (includeUnknownOrigin || origin === NPM_REGISTRY_URL$1) {
|
|
112
|
+
details.push({
|
|
113
|
+
node: pkgNode,
|
|
114
|
+
origin,
|
|
115
|
+
existing: pkgNode
|
|
116
|
+
});
|
|
117
|
+
}
|
|
148
118
|
}
|
|
149
119
|
}
|
|
150
|
-
return
|
|
120
|
+
return details;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const {
|
|
124
|
+
API_V0_URL,
|
|
125
|
+
abortSignal: abortSignal$2
|
|
126
|
+
} = constants;
|
|
127
|
+
async function* batchScan(pkgIds) {
|
|
128
|
+
const req = https.request(`${API_V0_URL}/purl?alerts=true`, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: {
|
|
131
|
+
Authorization: `Basic ${Buffer.from(`${socketUrl.getPublicToken()}:`).toString('base64url')}`
|
|
132
|
+
},
|
|
133
|
+
signal: abortSignal$2
|
|
134
|
+
}).end(JSON.stringify({
|
|
135
|
+
components: pkgIds.map(id => ({
|
|
136
|
+
purl: `pkg:npm/${id}`
|
|
137
|
+
}))
|
|
138
|
+
}));
|
|
139
|
+
const {
|
|
140
|
+
0: res
|
|
141
|
+
} = await events.once(req, 'response');
|
|
142
|
+
const ok = res.statusCode >= 200 && res.statusCode <= 299;
|
|
143
|
+
if (!ok) {
|
|
144
|
+
throw new Error(`Socket API Error: ${res.statusCode}`);
|
|
145
|
+
}
|
|
146
|
+
const rli = readline.createInterface(res);
|
|
147
|
+
for await (const line of rli) {
|
|
148
|
+
yield JSON.parse(line);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function isArtifactAlertCveFixable(alert) {
|
|
152
|
+
const {
|
|
153
|
+
type
|
|
154
|
+
} = alert;
|
|
155
|
+
return (type === 'cve' || type === 'mediumCVE' || type === 'mildCVE' || type === 'criticalCVE') && !!alert.props?.['firstPatchedVersionIdentifier'] && !!alert.props?.['vulnerableVersionRange'];
|
|
156
|
+
}
|
|
157
|
+
function isArtifactAlertFixable(alert) {
|
|
158
|
+
return alert.type === 'socketUpgradeAvailable' || isArtifactAlertCveFixable(alert);
|
|
151
159
|
}
|
|
152
160
|
|
|
153
161
|
const {
|
|
@@ -165,37 +173,6 @@ const WARN_UX = {
|
|
|
165
173
|
block: false,
|
|
166
174
|
display: true
|
|
167
175
|
};
|
|
168
|
-
function findSocketYmlSync() {
|
|
169
|
-
let prevDir = null;
|
|
170
|
-
let dir = process.cwd();
|
|
171
|
-
while (dir !== prevDir) {
|
|
172
|
-
let ymlPath = path.join(dir, 'socket.yml');
|
|
173
|
-
let yml = maybeReadfileSync(ymlPath);
|
|
174
|
-
if (yml === undefined) {
|
|
175
|
-
ymlPath = path.join(dir, 'socket.yaml');
|
|
176
|
-
yml = maybeReadfileSync(ymlPath);
|
|
177
|
-
}
|
|
178
|
-
if (typeof yml === 'string') {
|
|
179
|
-
try {
|
|
180
|
-
return {
|
|
181
|
-
path: ymlPath,
|
|
182
|
-
parsed: config.parseSocketConfig(yml)
|
|
183
|
-
};
|
|
184
|
-
} catch {
|
|
185
|
-
throw new Error(`Found file but was unable to parse ${ymlPath}`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
prevDir = dir;
|
|
189
|
-
dir = path.join(dir, '..');
|
|
190
|
-
}
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
function maybeReadfileSync(filepath) {
|
|
194
|
-
try {
|
|
195
|
-
return fs.readFileSync(filepath, 'utf8');
|
|
196
|
-
} catch {}
|
|
197
|
-
return undefined;
|
|
198
|
-
}
|
|
199
176
|
|
|
200
177
|
// Iterates over all entries with ordered issue rule for deferral. Iterates over
|
|
201
178
|
// all issue rules and finds the first defined value that does not defer otherwise
|
|
@@ -374,7 +351,7 @@ void (async () => {
|
|
|
374
351
|
settings.entries.splice(i, 1);
|
|
375
352
|
}
|
|
376
353
|
}
|
|
377
|
-
const socketYml = findSocketYmlSync();
|
|
354
|
+
const socketYml = socketUrl.findSocketYmlSync();
|
|
378
355
|
if (socketYml) {
|
|
379
356
|
settings.entries.push({
|
|
380
357
|
start: socketYml.path,
|
|
@@ -394,32 +371,7 @@ void (async () => {
|
|
|
394
371
|
_uxLookup = createAlertUXLookup(settings);
|
|
395
372
|
})();
|
|
396
373
|
|
|
397
|
-
const
|
|
398
|
-
NODE_MODULES,
|
|
399
|
-
SOCKET_CLI_ISSUES_URL
|
|
400
|
-
} = constants;
|
|
401
|
-
const npmEntrypoint = fs.realpathSync.native(process.argv[1]);
|
|
402
|
-
const npmRootPath = pathResolve.findRoot(path.dirname(npmEntrypoint));
|
|
403
|
-
if (npmRootPath === undefined) {
|
|
404
|
-
console.error(`Unable to find npm CLI install directory.
|
|
405
|
-
Searched parent directories of ${npmEntrypoint}.
|
|
406
|
-
|
|
407
|
-
This is may be a bug with socket-npm related to changes to the npm CLI.
|
|
408
|
-
Please report to ${SOCKET_CLI_ISSUES_URL}.`);
|
|
409
|
-
// The exit code 127 indicates that the command or binary being executed
|
|
410
|
-
// could not be found.
|
|
411
|
-
process.exit(127);
|
|
412
|
-
}
|
|
413
|
-
const npmNmPath = path.join(npmRootPath, NODE_MODULES);
|
|
414
|
-
const arboristPkgPath = path.join(npmNmPath, '@npmcli/arborist');
|
|
415
|
-
const arboristClassPath = path.join(arboristPkgPath, 'lib/arborist/index.js');
|
|
416
|
-
const arboristDepValidPath = path.join(arboristPkgPath, 'lib/dep-valid.js');
|
|
417
|
-
const arboristEdgeClassPath = path.join(arboristPkgPath, 'lib/edge.js');
|
|
418
|
-
const arboristNodeClassPath = path.join(arboristPkgPath, 'lib/node.js');
|
|
419
|
-
const arboristOverrideSetClassPath = path.join(arboristPkgPath, 'lib/override-set.js');
|
|
420
|
-
const pacotePath = path.join(npmNmPath, 'pacote');
|
|
421
|
-
|
|
422
|
-
const depValid = require(arboristDepValidPath);
|
|
374
|
+
const depValid = require(npmPaths.getArboristDepValidPath());
|
|
423
375
|
|
|
424
376
|
const {
|
|
425
377
|
UNDEFINED_TOKEN
|
|
@@ -449,6 +401,7 @@ function tryRequire(...ids) {
|
|
|
449
401
|
let _log = UNDEFINED_TOKEN;
|
|
450
402
|
function getLogger() {
|
|
451
403
|
if (_log === UNDEFINED_TOKEN) {
|
|
404
|
+
const npmNmPath = npmPaths.getNpmNodeModulesPath();
|
|
452
405
|
_log = tryRequire([path.join(npmNmPath, 'proc-log/lib/index.js'),
|
|
453
406
|
// The proc-log DefinitelyTyped definition is incorrect. The type definition
|
|
454
407
|
// is really that of its export log.
|
|
@@ -460,7 +413,7 @@ function getLogger() {
|
|
|
460
413
|
const {
|
|
461
414
|
LOOP_SENTINEL: LOOP_SENTINEL$1
|
|
462
415
|
} = constants;
|
|
463
|
-
const OverrideSet = require(
|
|
416
|
+
const OverrideSet = require(npmPaths.getArboristOverrideSetClassPath());
|
|
464
417
|
|
|
465
418
|
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
466
419
|
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:
|
|
@@ -597,7 +550,7 @@ class SafeOverrideSet extends OverrideSet {
|
|
|
597
550
|
}
|
|
598
551
|
}
|
|
599
552
|
|
|
600
|
-
const Node = require(
|
|
553
|
+
const Node = require(npmPaths.getArboristNodeClassPath());
|
|
601
554
|
|
|
602
555
|
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
|
|
603
556
|
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:
|
|
@@ -870,7 +823,7 @@ class SafeNode extends Node {
|
|
|
870
823
|
}
|
|
871
824
|
}
|
|
872
825
|
|
|
873
|
-
const Edge = require(
|
|
826
|
+
const Edge = require(npmPaths.getArboristEdgeClassPath());
|
|
874
827
|
|
|
875
828
|
// The Edge class makes heavy use of private properties which subclasses do NOT
|
|
876
829
|
// have access to. So we have to recreate any functionality that relies on those
|
|
@@ -1083,7 +1036,7 @@ class SafeEdge extends Edge {
|
|
|
1083
1036
|
this.#safeError = null;
|
|
1084
1037
|
}
|
|
1085
1038
|
// Patch adding "else if" condition based on
|
|
1086
|
-
// https://github.com/npm/cli/pull/7025
|
|
1039
|
+
// https://github.com/npm/cli/pull/7025.
|
|
1087
1040
|
else if (oldOverrideSet) {
|
|
1088
1041
|
// Propagate the new override set to the target node.
|
|
1089
1042
|
this.#safeTo.updateOverridesEdgeInRemoved(oldOverrideSet);
|
|
@@ -1134,22 +1087,25 @@ class SafeEdge extends Edge {
|
|
|
1134
1087
|
}
|
|
1135
1088
|
}
|
|
1136
1089
|
|
|
1137
|
-
const pacote = require(pacotePath);
|
|
1138
1090
|
const {
|
|
1139
1091
|
LOOP_SENTINEL,
|
|
1140
1092
|
NPM,
|
|
1141
1093
|
NPM_REGISTRY_URL,
|
|
1142
1094
|
SOCKET_CLI_FIX_PACKAGE_LOCK_FILE,
|
|
1143
1095
|
SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE,
|
|
1144
|
-
abortSignal
|
|
1096
|
+
abortSignal,
|
|
1097
|
+
kInternalsSymbol,
|
|
1098
|
+
[kInternalsSymbol]: {
|
|
1099
|
+
getIPC
|
|
1100
|
+
}
|
|
1145
1101
|
} = constants;
|
|
1146
1102
|
const formatter = new socketUrl.ColorOrMarkdown(false);
|
|
1147
|
-
function findBestPatchVersion(name, availableVersions, currentMajorVersion,
|
|
1103
|
+
function findBestPatchVersion(name, availableVersions, currentMajorVersion, vulnerableVersionRange, _firstPatchedVersionIdentifier) {
|
|
1148
1104
|
const manifestVersion = registry.getManifestData(NPM, name)?.version;
|
|
1149
1105
|
// Filter versions that are within the current major version and are not in the vulnerable range
|
|
1150
1106
|
const eligibleVersions = availableVersions.filter(version => {
|
|
1151
1107
|
const isSameMajor = semver.major(version) === currentMajorVersion;
|
|
1152
|
-
const isNotVulnerable = !semver.satisfies(version,
|
|
1108
|
+
const isNotVulnerable = !semver.satisfies(version, vulnerableVersionRange);
|
|
1153
1109
|
if (isSameMajor && isNotVulnerable) {
|
|
1154
1110
|
return true;
|
|
1155
1111
|
}
|
|
@@ -1161,22 +1117,22 @@ function findBestPatchVersion(name, availableVersions, currentMajorVersion, vuln
|
|
|
1161
1117
|
// Use semver to find the max satisfying version.
|
|
1162
1118
|
return semver.maxSatisfying(eligibleVersions, '*');
|
|
1163
1119
|
}
|
|
1164
|
-
function
|
|
1120
|
+
function findPackageNodes(tree, packageName) {
|
|
1165
1121
|
const queue = [{
|
|
1166
1122
|
node: tree
|
|
1167
1123
|
}];
|
|
1124
|
+
const matches = [];
|
|
1168
1125
|
let sentinel = 0;
|
|
1169
1126
|
while (queue.length) {
|
|
1170
1127
|
if (sentinel++ === LOOP_SENTINEL) {
|
|
1171
|
-
throw new Error('Detected infinite loop in
|
|
1128
|
+
throw new Error('Detected infinite loop in findPackageNodes');
|
|
1172
1129
|
}
|
|
1173
1130
|
const {
|
|
1174
1131
|
node: currentNode
|
|
1175
1132
|
} = queue.pop();
|
|
1176
1133
|
const node = currentNode.children.get(packageName);
|
|
1177
1134
|
if (node) {
|
|
1178
|
-
|
|
1179
|
-
return node;
|
|
1135
|
+
matches.push(node);
|
|
1180
1136
|
}
|
|
1181
1137
|
const children = [...currentNode.children.values()];
|
|
1182
1138
|
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
@@ -1185,18 +1141,19 @@ function findPackage(tree, packageName) {
|
|
|
1185
1141
|
});
|
|
1186
1142
|
}
|
|
1187
1143
|
}
|
|
1188
|
-
return
|
|
1144
|
+
return matches;
|
|
1189
1145
|
}
|
|
1190
|
-
async function getPackagesAlerts(
|
|
1146
|
+
async function getPackagesAlerts(details, options) {
|
|
1191
1147
|
let {
|
|
1192
1148
|
length: remaining
|
|
1193
|
-
} =
|
|
1149
|
+
} = details;
|
|
1194
1150
|
const packageAlerts = [];
|
|
1195
1151
|
if (!remaining) {
|
|
1196
1152
|
return packageAlerts;
|
|
1197
1153
|
}
|
|
1198
1154
|
const {
|
|
1199
|
-
|
|
1155
|
+
includeExisting = false,
|
|
1156
|
+
includeUnfixable = true,
|
|
1200
1157
|
output
|
|
1201
1158
|
} = {
|
|
1202
1159
|
__proto__: null,
|
|
@@ -1208,7 +1165,7 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1208
1165
|
const getText = spinner$1 ? () => `Looking up data for ${remaining} packages` : () => '';
|
|
1209
1166
|
spinner$1?.start(getText());
|
|
1210
1167
|
try {
|
|
1211
|
-
for await (const artifact of batchScan(
|
|
1168
|
+
for await (const artifact of batchScan(arrays.arrayUnique(details.map(d => d.node.pkgid)))) {
|
|
1212
1169
|
if (!artifact.name || !artifact.version || !artifact.alerts?.length) {
|
|
1213
1170
|
continue;
|
|
1214
1171
|
}
|
|
@@ -1217,7 +1174,6 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1217
1174
|
} = artifact;
|
|
1218
1175
|
const name = packages.resolvePackageName(artifact);
|
|
1219
1176
|
const id = `${name}@${artifact.version}`;
|
|
1220
|
-
let blocked = false;
|
|
1221
1177
|
let displayWarning = false;
|
|
1222
1178
|
let alerts = [];
|
|
1223
1179
|
for (const alert of artifact.alerts) {
|
|
@@ -1231,15 +1187,12 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1231
1187
|
type: alert.type
|
|
1232
1188
|
}
|
|
1233
1189
|
});
|
|
1234
|
-
if (ux.block) {
|
|
1235
|
-
blocked = true;
|
|
1236
|
-
}
|
|
1237
1190
|
if (ux.display && output) {
|
|
1238
1191
|
displayWarning = true;
|
|
1239
1192
|
}
|
|
1240
1193
|
if (ux.block || ux.display) {
|
|
1241
|
-
const
|
|
1242
|
-
if (
|
|
1194
|
+
const fixable = isArtifactAlertFixable(alert);
|
|
1195
|
+
if (includeUnfixable || fixable) {
|
|
1243
1196
|
alerts.push({
|
|
1244
1197
|
name,
|
|
1245
1198
|
version,
|
|
@@ -1247,38 +1200,27 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1247
1200
|
type: alert.type,
|
|
1248
1201
|
block: ux.block,
|
|
1249
1202
|
raw: alert,
|
|
1250
|
-
fixable
|
|
1203
|
+
fixable
|
|
1251
1204
|
});
|
|
1252
1205
|
}
|
|
1253
1206
|
// Lazily access constants.IPC.
|
|
1254
|
-
if (
|
|
1207
|
+
if (includeExisting && !constants.IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]) {
|
|
1255
1208
|
// Before we ask about problematic issues, check to see if they
|
|
1256
1209
|
// already existed in the old version if they did, be quiet.
|
|
1257
|
-
const existing =
|
|
1210
|
+
const existing = details.find(d => d.existing?.pkgid.startsWith(`${name}@`))?.existing;
|
|
1258
1211
|
if (existing) {
|
|
1259
1212
|
const oldArtifact =
|
|
1260
1213
|
// eslint-disable-next-line no-await-in-loop
|
|
1261
|
-
(await batchScan([existing]).next()).value;
|
|
1214
|
+
(await batchScan([existing.pkgid]).next()).value;
|
|
1262
1215
|
if (oldArtifact?.alerts?.length) {
|
|
1263
1216
|
alerts = alerts.filter(({
|
|
1264
1217
|
type
|
|
1265
|
-
}) => !oldArtifact.alerts
|
|
1218
|
+
}) => !oldArtifact.alerts.find(a => a.type === type));
|
|
1266
1219
|
}
|
|
1267
1220
|
}
|
|
1268
1221
|
}
|
|
1269
1222
|
}
|
|
1270
1223
|
}
|
|
1271
|
-
if (!blocked) {
|
|
1272
|
-
const pkg = pkgs.find(p => p.pkgid === id);
|
|
1273
|
-
if (pkg) {
|
|
1274
|
-
await pacote.tarball.stream(id, stream => {
|
|
1275
|
-
stream.resume();
|
|
1276
|
-
return stream.promise();
|
|
1277
|
-
}, {
|
|
1278
|
-
...safeArb[kCtorArgs][0]
|
|
1279
|
-
});
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
1224
|
if (displayWarning && spinner$1) {
|
|
1283
1225
|
spinner$1.stop(`(socket) ${formatter.hyperlink(id, socketUrl.getSocketDevPackageOverviewUrl(NPM, name, version))} contains risks:`);
|
|
1284
1226
|
}
|
|
@@ -1309,7 +1251,7 @@ async function getPackagesAlerts(safeArb, pkgs, options) {
|
|
|
1309
1251
|
packageAlerts.push(...alerts);
|
|
1310
1252
|
}
|
|
1311
1253
|
} catch (e) {
|
|
1312
|
-
|
|
1254
|
+
npmPaths.debugLog(e);
|
|
1313
1255
|
} finally {
|
|
1314
1256
|
spinner$1?.stop();
|
|
1315
1257
|
}
|
|
@@ -1324,194 +1266,181 @@ function getTranslations() {
|
|
|
1324
1266
|
}
|
|
1325
1267
|
return _translations;
|
|
1326
1268
|
}
|
|
1327
|
-
function
|
|
1328
|
-
let
|
|
1269
|
+
async function updateAdvisoryDependencies(arb, alerts) {
|
|
1270
|
+
let patchDataByPkg;
|
|
1329
1271
|
for (const alert of alerts) {
|
|
1330
|
-
if (!
|
|
1272
|
+
if (!isArtifactAlertCveFixable(alert.raw)) {
|
|
1331
1273
|
continue;
|
|
1332
1274
|
}
|
|
1275
|
+
if (!patchDataByPkg) {
|
|
1276
|
+
patchDataByPkg = {};
|
|
1277
|
+
}
|
|
1333
1278
|
const {
|
|
1334
1279
|
name
|
|
1335
1280
|
} = alert;
|
|
1336
|
-
if (!
|
|
1337
|
-
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
title: props?.title,
|
|
1347
|
-
severity: alert.raw?.severity?.toLowerCase(),
|
|
1348
|
-
vulnerable_versions: props?.vulnerableVersionRange,
|
|
1349
|
-
cwe: props?.cwes,
|
|
1350
|
-
cvss: props?.csvs,
|
|
1351
|
-
name
|
|
1281
|
+
if (!patchDataByPkg[name]) {
|
|
1282
|
+
patchDataByPkg[name] = [];
|
|
1283
|
+
}
|
|
1284
|
+
const {
|
|
1285
|
+
firstPatchedVersionIdentifier,
|
|
1286
|
+
vulnerableVersionRange
|
|
1287
|
+
} = alert.raw.props;
|
|
1288
|
+
patchDataByPkg[name].push({
|
|
1289
|
+
firstPatchedVersionIdentifier,
|
|
1290
|
+
vulnerableVersionRange
|
|
1352
1291
|
});
|
|
1353
1292
|
}
|
|
1354
|
-
|
|
1355
|
-
}
|
|
1356
|
-
async function updateAdvisoryDependencies(arb, alerts) {
|
|
1357
|
-
const report = packageAlertsToReport(alerts);
|
|
1358
|
-
if (!report) {
|
|
1293
|
+
if (!patchDataByPkg) {
|
|
1359
1294
|
// No advisories to process.
|
|
1360
1295
|
return;
|
|
1361
1296
|
}
|
|
1362
1297
|
await arb.buildIdealTree();
|
|
1363
1298
|
const tree = arb.idealTree;
|
|
1364
|
-
for (const name of Object.keys(
|
|
1365
|
-
const
|
|
1366
|
-
|
|
1367
|
-
if (!node) {
|
|
1368
|
-
// Package not found in the tree.
|
|
1299
|
+
for (const name of Object.keys(patchDataByPkg)) {
|
|
1300
|
+
const nodes = findPackageNodes(tree, name);
|
|
1301
|
+
if (!nodes.length) {
|
|
1369
1302
|
continue;
|
|
1370
1303
|
}
|
|
1371
|
-
const {
|
|
1372
|
-
version
|
|
1373
|
-
} = node;
|
|
1374
|
-
const majorVerNum = semver.major(version);
|
|
1375
|
-
|
|
1376
1304
|
// Fetch packument to get available versions.
|
|
1377
1305
|
// eslint-disable-next-line no-await-in-loop
|
|
1378
1306
|
const packument = await packages.fetchPackagePackument(name);
|
|
1379
|
-
const
|
|
1380
|
-
for (const advisory of advisories) {
|
|
1307
|
+
for (const node of nodes) {
|
|
1381
1308
|
const {
|
|
1382
|
-
|
|
1383
|
-
} =
|
|
1384
|
-
|
|
1385
|
-
const
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1309
|
+
version
|
|
1310
|
+
} = node;
|
|
1311
|
+
const majorVerNum = semver.major(version);
|
|
1312
|
+
const availableVersions = packument ? Object.keys(packument.versions) : [];
|
|
1313
|
+
const patchData = patchDataByPkg[name];
|
|
1314
|
+
for (const {
|
|
1315
|
+
firstPatchedVersionIdentifier,
|
|
1316
|
+
vulnerableVersionRange
|
|
1317
|
+
} of patchData) {
|
|
1318
|
+
// Find the highest non-vulnerable version within the same major range
|
|
1319
|
+
const targetVersion = findBestPatchVersion(name, availableVersions, majorVerNum, vulnerableVersionRange);
|
|
1320
|
+
const targetPackument = targetVersion ? packument.versions[targetVersion] : undefined;
|
|
1321
|
+
// Check !targetVersion to make TypeScript happy.
|
|
1322
|
+
if (!targetVersion || !targetPackument) {
|
|
1323
|
+
// No suitable patch version found.
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
// Use Object.defineProperty to override the version.
|
|
1327
|
+
Object.defineProperty(node, 'version', {
|
|
1328
|
+
configurable: true,
|
|
1329
|
+
enumerable: true,
|
|
1330
|
+
get: () => targetVersion
|
|
1331
|
+
});
|
|
1332
|
+
node.package.version = targetVersion;
|
|
1333
|
+
// Update resolved and clear integrity for the new version.
|
|
1334
|
+
node.resolved = `${NPM_REGISTRY_URL}/${name}/-/${name}-${targetVersion}.tgz`;
|
|
1335
|
+
if (node.integrity) {
|
|
1336
|
+
delete node.integrity;
|
|
1337
|
+
}
|
|
1338
|
+
if ('deprecated' in targetPackument) {
|
|
1339
|
+
node.package['deprecated'] = targetPackument.deprecated;
|
|
1340
|
+
} else {
|
|
1341
|
+
delete node.package['deprecated'];
|
|
1342
|
+
}
|
|
1343
|
+
const newDeps = {
|
|
1344
|
+
...targetPackument.dependencies
|
|
1345
|
+
};
|
|
1346
|
+
const {
|
|
1347
|
+
dependencies: oldDeps
|
|
1348
|
+
} = node.package;
|
|
1349
|
+
node.package.dependencies = newDeps;
|
|
1350
|
+
if (oldDeps) {
|
|
1351
|
+
for (const oldDepName of Object.keys(oldDeps)) {
|
|
1352
|
+
if (!objects.hasOwn(newDeps, oldDepName)) {
|
|
1353
|
+
node.edgesOut.get(oldDepName)?.detach();
|
|
1354
|
+
}
|
|
1421
1355
|
}
|
|
1422
1356
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
}
|
|
1357
|
+
for (const newDepName of Object.keys(newDeps)) {
|
|
1358
|
+
if (!objects.hasOwn(oldDeps, newDepName)) {
|
|
1359
|
+
node.addEdgeOut(new Edge({
|
|
1360
|
+
from: node,
|
|
1361
|
+
name: newDepName,
|
|
1362
|
+
spec: newDeps[newDepName],
|
|
1363
|
+
type: 'prod'
|
|
1364
|
+
}));
|
|
1365
|
+
}
|
|
1432
1366
|
}
|
|
1433
1367
|
}
|
|
1434
1368
|
}
|
|
1435
1369
|
}
|
|
1436
1370
|
}
|
|
1371
|
+
const kRiskyReify = Symbol('riskyReify');
|
|
1437
1372
|
async function reify(...args) {
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1373
|
+
const IPC = await getIPC();
|
|
1374
|
+
// We are assuming `this[_diffTrees]()` has been called by `super.reify(...)`:
|
|
1375
|
+
// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/reify.js#L141
|
|
1376
|
+
let needInfoOn = getPackagesToQueryFromDiff(this.diff, {
|
|
1377
|
+
includeUnchanged: !!IPC[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]
|
|
1378
|
+
});
|
|
1379
|
+
if (!needInfoOn.length) {
|
|
1440
1380
|
// Nothing to check, hmmm already installed or all private?
|
|
1441
1381
|
return await this[kRiskyReify](...args);
|
|
1442
1382
|
}
|
|
1443
|
-
// Lazily access constants.IPC.
|
|
1444
1383
|
const {
|
|
1445
1384
|
[SOCKET_CLI_FIX_PACKAGE_LOCK_FILE]: bypassConfirms,
|
|
1446
1385
|
[SOCKET_CLI_UPDATE_OVERRIDES_IN_PACKAGE_LOCK_FILE]: bypassAlerts
|
|
1447
|
-
} =
|
|
1386
|
+
} = IPC;
|
|
1448
1387
|
const {
|
|
1449
1388
|
stderr: output,
|
|
1450
1389
|
stdin: input
|
|
1451
1390
|
} = process;
|
|
1452
|
-
let alerts
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1391
|
+
let alerts = bypassAlerts ? [] : await getPackagesAlerts(needInfoOn, {
|
|
1392
|
+
output
|
|
1393
|
+
});
|
|
1394
|
+
if (alerts.length && !bypassConfirms && !(await prompts.confirm({
|
|
1395
|
+
message: 'Accept risks of installing these packages?',
|
|
1396
|
+
default: false
|
|
1397
|
+
}, {
|
|
1398
|
+
input,
|
|
1399
|
+
output,
|
|
1400
|
+
signal: abortSignal
|
|
1401
|
+
}))) {
|
|
1402
|
+
throw new Error('Socket npm exiting due to risks');
|
|
1403
|
+
}
|
|
1404
|
+
if (!alerts.length || !bypassConfirms && !(await prompts.confirm({
|
|
1405
|
+
message: 'Try to fix alerts?',
|
|
1406
|
+
default: true
|
|
1407
|
+
}, {
|
|
1408
|
+
input,
|
|
1409
|
+
output,
|
|
1410
|
+
signal: abortSignal
|
|
1411
|
+
}))) {
|
|
1412
|
+
return await this[kRiskyReify](...args);
|
|
1413
|
+
}
|
|
1414
|
+
const prev = new Set(alerts.map(a => a.key));
|
|
1415
|
+
let ret;
|
|
1416
|
+
/* eslint-disable no-await-in-loop */
|
|
1417
|
+
while (alerts.length > 0) {
|
|
1418
|
+
await updateAdvisoryDependencies(this, alerts);
|
|
1419
|
+
ret = await this[kRiskyReify](...args);
|
|
1420
|
+
await this.loadActual();
|
|
1421
|
+
await this.buildIdealTree();
|
|
1422
|
+
needInfoOn = getPackagesToQueryFromDiff(this.diff, {
|
|
1423
|
+
includeUnchanged: true
|
|
1467
1424
|
});
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
})));
|
|
1478
|
-
if (fix) {
|
|
1479
|
-
let ret;
|
|
1480
|
-
const prev = new Set(alerts?.map(a => a.key));
|
|
1481
|
-
/* eslint-disable no-await-in-loop */
|
|
1482
|
-
while (alerts.length > 0) {
|
|
1483
|
-
await updateAdvisoryDependencies(this, alerts);
|
|
1484
|
-
ret = await this[kRiskyReify](...args);
|
|
1485
|
-
await this.loadActual();
|
|
1486
|
-
await this.buildIdealTree();
|
|
1487
|
-
alerts = await getPackagesAlerts(this, await walk(this.diff, {
|
|
1488
|
-
fix: true
|
|
1489
|
-
}), {
|
|
1490
|
-
fixable: true
|
|
1491
|
-
});
|
|
1492
|
-
alerts = alerts.filter(a => {
|
|
1493
|
-
const {
|
|
1494
|
-
key
|
|
1495
|
-
} = a;
|
|
1496
|
-
if (prev.has(key)) {
|
|
1497
|
-
return false;
|
|
1498
|
-
}
|
|
1499
|
-
prev.add(key);
|
|
1500
|
-
return true;
|
|
1501
|
-
});
|
|
1425
|
+
alerts = (await getPackagesAlerts(needInfoOn, {
|
|
1426
|
+
includeExisting: true,
|
|
1427
|
+
includeUnfixable: true
|
|
1428
|
+
})).filter(({
|
|
1429
|
+
key
|
|
1430
|
+
}) => {
|
|
1431
|
+
const unseen = !prev.has(key);
|
|
1432
|
+
if (unseen) {
|
|
1433
|
+
prev.add(key);
|
|
1502
1434
|
}
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
}
|
|
1506
|
-
return await this[kRiskyReify](...args);
|
|
1507
|
-
} else {
|
|
1508
|
-
throw new Error('Socket npm exiting due to risks');
|
|
1435
|
+
return unseen;
|
|
1436
|
+
});
|
|
1509
1437
|
}
|
|
1438
|
+
/* eslint-enable no-await-in-loop */
|
|
1439
|
+
return ret;
|
|
1510
1440
|
}
|
|
1511
1441
|
|
|
1512
|
-
const Arborist = require(
|
|
1442
|
+
const Arborist = require(npmPaths.getArboristClassPath());
|
|
1513
1443
|
const kCtorArgs = Symbol('ctorArgs');
|
|
1514
|
-
const kRiskyReify = Symbol('riskyReify');
|
|
1515
1444
|
|
|
1516
1445
|
// Implementation code not related to our custom behavior is based on
|
|
1517
1446
|
// https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/arborist/index.js:
|
|
@@ -1556,8 +1485,6 @@ class SafeArborist extends Arborist {
|
|
|
1556
1485
|
options.dryRun = true;
|
|
1557
1486
|
options.save = false;
|
|
1558
1487
|
options.saveBundle = false;
|
|
1559
|
-
// TODO: Make this deal with any refactor to private fields by punching the
|
|
1560
|
-
// class itself.
|
|
1561
1488
|
await super.reify(...args);
|
|
1562
1489
|
options.dryRun = old.dryRun;
|
|
1563
1490
|
options.save = old.save;
|
|
@@ -1567,17 +1494,19 @@ class SafeArborist extends Arborist {
|
|
|
1567
1494
|
}
|
|
1568
1495
|
|
|
1569
1496
|
function installSafeArborist() {
|
|
1497
|
+
// Override '@npmcli/arborist' module exports with patched variants based on
|
|
1498
|
+
// https://github.com/npm/cli/pull/7025.
|
|
1570
1499
|
const cache = require.cache;
|
|
1571
|
-
cache[
|
|
1500
|
+
cache[npmPaths.getArboristClassPath()] = {
|
|
1572
1501
|
exports: SafeArborist
|
|
1573
1502
|
};
|
|
1574
|
-
cache[
|
|
1503
|
+
cache[npmPaths.getArboristEdgeClassPath()] = {
|
|
1575
1504
|
exports: SafeEdge
|
|
1576
1505
|
};
|
|
1577
|
-
cache[
|
|
1506
|
+
cache[npmPaths.getArboristNodeClassPath()] = {
|
|
1578
1507
|
exports: SafeNode
|
|
1579
1508
|
};
|
|
1580
|
-
cache[
|
|
1509
|
+
cache[npmPaths.getArboristOverrideSetClassPath()] = {
|
|
1581
1510
|
exports: SafeOverrideSet
|
|
1582
1511
|
};
|
|
1583
1512
|
}
|