socket 1.1.0 → 1.1.2

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.
Files changed (52) hide show
  1. package/dist/cli.js +465 -404
  2. package/dist/cli.js.map +1 -1
  3. package/dist/constants.js +3 -3
  4. package/dist/constants.js.map +1 -1
  5. package/dist/flags.js +5 -6
  6. package/dist/flags.js.map +1 -1
  7. package/dist/shadow-npm-bin.js +4 -4
  8. package/dist/shadow-npm-bin.js.map +1 -1
  9. package/dist/shadow-npm-inject.js +5 -8
  10. package/dist/shadow-npm-inject.js.map +1 -1
  11. package/dist/socket-completion.bash +1 -1
  12. package/dist/tsconfig.dts.tsbuildinfo +1 -1
  13. package/dist/types/commands/fix/cmd-fix.d.mts.map +1 -1
  14. package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
  15. package/dist/types/commands/fix/{fix-env-helpers.d.mts → env-helpers.d.mts} +1 -1
  16. package/dist/types/commands/fix/env-helpers.d.mts.map +1 -0
  17. package/dist/types/commands/fix/git.d.mts +13 -0
  18. package/dist/types/commands/fix/git.d.mts.map +1 -0
  19. package/dist/types/commands/fix/pull-request.d.mts +10 -53
  20. package/dist/types/commands/fix/pull-request.d.mts.map +1 -1
  21. package/dist/types/commands/patch/cmd-patch.d.mts.map +1 -1
  22. package/dist/types/commands/patch/handle-patch.d.mts +1 -1
  23. package/dist/types/commands/patch/handle-patch.d.mts.map +1 -1
  24. package/dist/types/commands/patch/manifest-schema.d.mts +34 -0
  25. package/dist/types/commands/patch/manifest-schema.d.mts.map +1 -0
  26. package/dist/types/commands/scan/fetch-supported-scan-file-names.d.mts +2 -0
  27. package/dist/types/commands/scan/fetch-supported-scan-file-names.d.mts.map +1 -1
  28. package/dist/types/flags.d.mts +9 -8
  29. package/dist/types/flags.d.mts.map +1 -1
  30. package/dist/types/shadow/npm/arborist/lib/arborist/index.d.mts.map +1 -1
  31. package/dist/types/shadow/npm/paths.d.mts +0 -1
  32. package/dist/types/shadow/npm/paths.d.mts.map +1 -1
  33. package/dist/types/utils/fs.d.mts +0 -1
  34. package/dist/types/utils/fs.d.mts.map +1 -1
  35. package/dist/types/utils/github.d.mts +38 -0
  36. package/dist/types/utils/github.d.mts.map +1 -0
  37. package/dist/types/utils/glob.d.mts +0 -1
  38. package/dist/types/utils/glob.d.mts.map +1 -1
  39. package/dist/utils.js +205 -18
  40. package/dist/utils.js.map +1 -1
  41. package/dist/vendor.js +3460 -140
  42. package/external/@socketsecurity/registry/external/libnpmpack.js +96569 -41361
  43. package/external/@socketsecurity/registry/external/pacote.js +77357 -68133
  44. package/external/@socketsecurity/registry/lib/fs.js +13 -27
  45. package/external/@socketsecurity/registry/lib/json.js +42 -0
  46. package/external/@socketsecurity/registry/manifest.json +4 -4
  47. package/package.json +8 -7
  48. package/dist/types/commands/fix/fix-branch-helpers.d.mts +0 -4
  49. package/dist/types/commands/fix/fix-branch-helpers.d.mts.map +0 -1
  50. package/dist/types/commands/fix/fix-env-helpers.d.mts.map +0 -1
  51. package/dist/types/commands/fix/socket-git.d.mts +0 -32
  52. package/dist/types/commands/fix/socket-git.d.mts.map +0 -1
package/dist/cli.js CHANGED
@@ -18,13 +18,14 @@ var spawn = require('../external/@socketsecurity/registry/lib/spawn');
18
18
  var fs$2 = require('../external/@socketsecurity/registry/lib/fs');
19
19
  var strings = require('../external/@socketsecurity/registry/lib/strings');
20
20
  var arrays = require('../external/@socketsecurity/registry/lib/arrays');
21
- var regexps = require('../external/@socketsecurity/registry/lib/regexps');
22
21
  var path$1 = require('../external/@socketsecurity/registry/lib/path');
23
22
  var shadowNpmBin = require('./shadow-npm-bin.js');
24
- var require$$10 = require('../external/@socketsecurity/registry/lib/objects');
23
+ var require$$11 = require('../external/@socketsecurity/registry/lib/objects');
25
24
  var registry = require('../external/@socketsecurity/registry');
26
25
  var packages = require('../external/@socketsecurity/registry/lib/packages');
27
- var require$$11 = require('../external/@socketsecurity/registry/lib/promises');
26
+ var require$$12 = require('../external/@socketsecurity/registry/lib/promises');
27
+ var regexps = require('../external/@socketsecurity/registry/lib/regexps');
28
+ var require$$0$1 = require('node:crypto');
28
29
  var require$$1 = require('node:util');
29
30
  var os = require('node:os');
30
31
  var promises = require('node:stream/promises');
@@ -923,7 +924,8 @@ async function fetchCreateOrgFullScan(packagePaths, orgSlug, config, options) {
923
924
 
924
925
  async function fetchSupportedScanFileNames(options) {
925
926
  const {
926
- sdkOpts
927
+ sdkOpts,
928
+ spinner
927
929
  } = {
928
930
  __proto__: null,
929
931
  ...options
@@ -934,7 +936,8 @@ async function fetchSupportedScanFileNames(options) {
934
936
  }
935
937
  const sockSdk = sockSdkCResult.data;
936
938
  return await utils.handleApiCall(sockSdk.getSupportedScanFiles(), {
937
- desc: 'supported scan file types'
939
+ desc: 'supported scan file types',
940
+ spinner
938
941
  });
939
942
  }
940
943
 
@@ -2154,7 +2157,12 @@ async function handleCreateNewScan({
2154
2157
  });
2155
2158
  logger.logger.info('Auto-generation finished. Proceeding with Scan creation.');
2156
2159
  }
2157
- const supportedFilesCResult = await fetchSupportedScanFileNames();
2160
+ const {
2161
+ spinner
2162
+ } = constants;
2163
+ const supportedFilesCResult = await fetchSupportedScanFileNames({
2164
+ spinner
2165
+ });
2158
2166
  if (!supportedFilesCResult.ok) {
2159
2167
  await outputCreateNewScan(supportedFilesCResult, {
2160
2168
  interactive,
@@ -2162,9 +2170,6 @@ async function handleCreateNewScan({
2162
2170
  });
2163
2171
  return;
2164
2172
  }
2165
- const {
2166
- spinner
2167
- } = constants;
2168
2173
  spinner.start('Searching for local files to include in scan...');
2169
2174
  const supportedFiles = supportedFilesCResult.data;
2170
2175
  const packagePaths = await utils.getPackageFilesForScan(targets, supportedFiles, {
@@ -3165,210 +3170,76 @@ const cmdConfig = {
3165
3170
  }
3166
3171
  };
3167
3172
 
3168
- function formatBranchName(name) {
3169
- return name.replace(/[^-a-zA-Z0-9/._-]+/g, '+');
3173
+ const GITHUB_ADVISORIES_URL = 'https://github.com/advisories';
3174
+ function getSocketFixBranchName(ghsaId) {
3175
+ return `socket/fix/${ghsaId}`;
3170
3176
  }
3171
- function createSocketBranchParser(options) {
3172
- const pattern = getSocketBranchPattern(options);
3173
- return function parse(branch) {
3174
- const match = pattern.exec(branch);
3175
- if (!match) {
3176
- return null;
3177
+ function getSocketFixBranchPattern(ghsaId) {
3178
+ return new RegExp(`^socket/fix/(${ghsaId ?? '.+'})$`);
3179
+ }
3180
+ function getSocketFixCommitMessage(ghsaId, details) {
3181
+ const summary = details?.summary;
3182
+ return `fix: ${ghsaId}${summary ? ` - ${summary}` : ''}`;
3183
+ }
3184
+ function getSocketFixPullRequestBody(ghsaIds, ghsaDetails) {
3185
+ const vulnCount = ghsaIds.length;
3186
+ if (vulnCount === 1) {
3187
+ const ghsaId = ghsaIds[0];
3188
+ const details = ghsaDetails?.get(ghsaId);
3189
+ const body = `[Socket](${constants.SOCKET_WEBSITE_URL}) fix for [${ghsaId}](${GITHUB_ADVISORIES_URL}/${ghsaId}).`;
3190
+ if (!details) {
3191
+ return body;
3177
3192
  }
3178
- const {
3179
- 1: type,
3180
- 2: workspace,
3181
- 3: fullName,
3182
- 4: version,
3183
- 5: newVersion
3184
- } = match;
3185
- return {
3186
- fullName,
3187
- newVersion: vendor.semverExports.coerce(newVersion.replaceAll('+', '.'))?.version,
3188
- type,
3189
- workspace,
3190
- version: vendor.semverExports.coerce(version.replaceAll('+', '.'))?.version
3191
- };
3192
- };
3193
+ const packages = details.vulnerabilities.nodes.map(v => `${v.package.name} (${v.package.ecosystem})`);
3194
+ return [body, '', '', `**Vulnerability Summary:** ${details.summary}`, '', `**Severity:** ${details.severity}`, '', `**Affected Packages:** ${arrays.joinAnd(packages)}`].join('\n');
3195
+ }
3196
+ return [`[Socket](${constants.SOCKET_WEBSITE_URL}) fixes for ${vulnCount} GHSAs.`, '', '**Fixed Vulnerabilities:**', ...ghsaIds.map(id => {
3197
+ const details = ghsaDetails?.get(id);
3198
+ const item = `- [${id}](${GITHUB_ADVISORIES_URL}/${id})`;
3199
+ if (details) {
3200
+ const packages = details.vulnerabilities.nodes.map(v => `${v.package.name}`);
3201
+ return `${item} - ${details.summary} (${arrays.joinAnd(packages)})`;
3202
+ }
3203
+ return item;
3204
+ })].join('\n');
3205
+ }
3206
+ function getSocketFixPullRequestTitle(ghsaIds) {
3207
+ const vulnCount = ghsaIds.length;
3208
+ return vulnCount === 1 ? `Fix for ${ghsaIds[0]}` : `Fixes for ${vulnCount} GHSAs`;
3193
3209
  }
3194
- createSocketBranchParser();
3195
- function getSocketBranchPattern(options) {
3210
+
3211
+ async function openSocketFixPr(owner, repo, branch, ghsaIds, options) {
3196
3212
  const {
3197
- newVersion,
3198
- purl,
3199
- workspace
3213
+ baseBranch = 'main',
3214
+ ghsaDetails
3200
3215
  } = {
3201
3216
  __proto__: null,
3202
3217
  ...options
3203
3218
  };
3204
- const purlObj = purl ? utils.getPurlObject(purl) : null;
3205
- const escType = purlObj ? regexps.escapeRegExp(purlObj.type) : '[^/]+';
3206
- const escWorkspace = workspace ? `${regexps.escapeRegExp(formatBranchName(workspace))}` : '.+';
3207
- const escMaybeNamespace = purlObj?.namespace ? `${regexps.escapeRegExp(formatBranchName(purlObj.namespace))}--` : '';
3208
- const escFullName = purlObj ? `${escMaybeNamespace}${regexps.escapeRegExp(formatBranchName(purlObj.name))}` : '[^/_]+';
3209
- const escVersion = purlObj ? regexps.escapeRegExp(formatBranchName(purlObj.version)) : '[^_]+';
3210
- const escNewVersion = newVersion ? regexps.escapeRegExp(formatBranchName(newVersion)) : '[^_]+';
3211
- return new RegExp(`^socket/(${escType})/(${escWorkspace})/(${escFullName})_(${escVersion})_(${escNewVersion})$`);
3212
- }
3213
-
3214
- let _octokit;
3215
- function getOctokit() {
3216
- if (_octokit === undefined) {
3217
- const {
3218
- SOCKET_CLI_GITHUB_TOKEN
3219
- } = constants.ENV;
3220
- if (!SOCKET_CLI_GITHUB_TOKEN) {
3221
- require$$9.debugFn('notice', 'miss: SOCKET_CLI_GITHUB_TOKEN env var');
3222
- }
3223
- const octokitOptions = {
3224
- auth: SOCKET_CLI_GITHUB_TOKEN,
3225
- baseUrl: constants.ENV.GITHUB_API_URL
3219
+ const octokit = utils.getOctokit();
3220
+ try {
3221
+ const octokitPullsCreateParams = {
3222
+ owner,
3223
+ repo,
3224
+ title: getSocketFixPullRequestTitle(ghsaIds),
3225
+ head: branch,
3226
+ base: baseBranch,
3227
+ body: getSocketFixPullRequestBody(ghsaIds, ghsaDetails)
3226
3228
  };
3227
3229
  require$$9.debugDir('inspect', {
3228
- octokitOptions
3229
- });
3230
- _octokit = new vendor.Octokit(octokitOptions);
3231
- }
3232
- return _octokit;
3233
- }
3234
- let _octokitGraphql;
3235
- function getOctokitGraphql() {
3236
- if (!_octokitGraphql) {
3237
- const {
3238
- SOCKET_CLI_GITHUB_TOKEN
3239
- } = constants.ENV;
3240
- if (!SOCKET_CLI_GITHUB_TOKEN) {
3241
- require$$9.debugFn('notice', 'miss: SOCKET_CLI_GITHUB_TOKEN env var');
3242
- }
3243
- _octokitGraphql = vendor.graphql2.defaults({
3244
- headers: {
3245
- authorization: `token ${SOCKET_CLI_GITHUB_TOKEN}`
3246
- }
3247
- });
3248
- }
3249
- return _octokitGraphql;
3250
- }
3251
- async function readCache(key,
3252
- // 5 minute in milliseconds time to live (TTL).
3253
- ttlMs = 5 * 60 * 1000) {
3254
- const cacheJsonPath = path.join(constants.githubCachePath, `${key}.json`);
3255
- const stat = fs$2.safeStatsSync(cacheJsonPath);
3256
- if (stat) {
3257
- const isExpired = Date.now() - stat.mtimeMs > ttlMs;
3258
- if (!isExpired) {
3259
- return await fs$2.readJson(cacheJsonPath);
3260
- }
3261
- }
3262
- return null;
3263
- }
3264
- async function writeCache(key, data) {
3265
- const {
3266
- githubCachePath
3267
- } = constants;
3268
- const cacheJsonPath = path.join(githubCachePath, `${key}.json`);
3269
- if (!fs$1.existsSync(githubCachePath)) {
3270
- await fs$1.promises.mkdir(githubCachePath, {
3271
- recursive: true
3272
- });
3273
- }
3274
- await fs$2.writeJson(cacheJsonPath, data);
3275
- }
3276
- async function cacheFetch(key, fetcher, ttlMs) {
3277
- // Optionally disable cache.
3278
- if (constants.ENV.DISABLE_GITHUB_CACHE) {
3279
- return await fetcher();
3280
- }
3281
- let data = await readCache(key, ttlMs);
3282
- if (!data) {
3283
- data = await fetcher();
3284
- await writeCache(key, data);
3285
- }
3286
- return data;
3287
- }
3288
- async function fetchGhsaDetails(ids) {
3289
- const results = new Map();
3290
- if (!ids.length) {
3291
- return results;
3292
- }
3293
- const octokitGraphql = getOctokitGraphql();
3294
- try {
3295
- const gqlCacheKey = `${ids.join('-')}-graphql-snapshot`;
3296
- const aliases = ids.map((id, index) => `advisory${index}: securityAdvisory(ghsaId: "${id}") {
3297
- ghsaId
3298
- summary
3299
- severity
3300
- publishedAt
3301
- withdrawnAt
3302
- vulnerabilities(first: 10) {
3303
- nodes {
3304
- package {
3305
- ecosystem
3306
- name
3307
- }
3308
- vulnerableVersionRange
3309
- }
3310
- }
3311
- }`).join('\n');
3312
- const gqlResp = await cacheFetch(gqlCacheKey, () => octokitGraphql(`
3313
- query {
3314
- ${aliases}
3315
- }
3316
- `));
3317
- for (let i = 0, {
3318
- length
3319
- } = ids; i < length; i += 1) {
3320
- const id = ids[i];
3321
- const advisoryKey = `advisory${i}`;
3322
- const advisory = gqlResp?.[advisoryKey];
3323
- if (advisory && advisory.ghsaId) {
3324
- results.set(id, advisory);
3325
- } else {
3326
- require$$9.debugFn('notice', `miss: no advisory found for ${id}`);
3327
- }
3328
- }
3329
- } catch (e) {
3330
- require$$9.debugFn('error', `Failed to fetch GHSA details: ${e?.message || 'Unknown error'}`);
3331
- }
3332
- return results;
3333
- }
3334
- async function enablePrAutoMerge({
3335
- node_id: prId
3336
- }) {
3337
- const octokitGraphql = getOctokitGraphql();
3338
- try {
3339
- const gqlResp = await octokitGraphql(`
3340
- mutation EnableAutoMerge($pullRequestId: ID!) {
3341
- enablePullRequestAutoMerge(input: {
3342
- pullRequestId: $pullRequestId,
3343
- mergeMethod: SQUASH
3344
- }) {
3345
- pullRequest {
3346
- number
3347
- }
3348
- }
3349
- }`, {
3350
- pullRequestId: prId
3230
+ octokitPullsCreateParams
3351
3231
  });
3352
- const respPrNumber = gqlResp?.enablePullRequestAutoMerge?.pullRequest?.number;
3353
- if (respPrNumber) {
3354
- return {
3355
- enabled: true
3356
- };
3357
- }
3232
+ return await octokit.pulls.create(octokitPullsCreateParams);
3358
3233
  } catch (e) {
3359
- if (e instanceof vendor.GraphqlResponseError && Array.isArray(e.errors) && e.errors.length) {
3360
- const details = e.errors.map(({
3361
- message: m
3362
- }) => m.trim());
3363
- return {
3364
- enabled: false,
3365
- details
3366
- };
3234
+ let message = `Failed to open pull request`;
3235
+ const errors = e instanceof vendor.RequestError ? e.response?.data?.['errors'] : undefined;
3236
+ if (Array.isArray(errors) && errors.length) {
3237
+ const details = errors.map(d => `- ${d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`}`).join('\n');
3238
+ message += `:\n${details}`;
3367
3239
  }
3240
+ require$$9.debugFn('error', message);
3368
3241
  }
3369
- return {
3370
- enabled: false
3371
- };
3242
+ return null;
3372
3243
  }
3373
3244
  async function getSocketPrs(owner, repo, options) {
3374
3245
  return (await getSocketPrsWithContext(owner, repo, options)).map(d => d.match);
@@ -3376,22 +3247,23 @@ async function getSocketPrs(owner, repo, options) {
3376
3247
  async function getSocketPrsWithContext(owner, repo, options) {
3377
3248
  const {
3378
3249
  author,
3250
+ ghsaId,
3379
3251
  states: statesValue = 'all'
3380
3252
  } = {
3381
3253
  __proto__: null,
3382
3254
  ...options
3383
3255
  };
3384
- const branchPattern = getSocketBranchPattern(options);
3256
+ const branchPattern = getSocketFixBranchPattern(ghsaId);
3385
3257
  const checkAuthor = strings.isNonEmptyString(author);
3386
- const octokit = getOctokit();
3387
- const octokitGraphql = getOctokitGraphql();
3258
+ const octokit = utils.getOctokit();
3259
+ const octokitGraphql = utils.getOctokitGraphql();
3388
3260
  const contextualMatches = [];
3389
3261
  const states = (typeof statesValue === 'string' ? statesValue.toLowerCase() === 'all' ? ['OPEN', 'CLOSED', 'MERGED'] : [statesValue] : statesValue).map(s => s.toUpperCase());
3390
3262
  try {
3391
3263
  // Optimistically fetch only the first 50 open PRs using GraphQL to minimize
3392
3264
  // API quota usage. Fallback to REST if no matching PRs are found.
3393
3265
  const gqlCacheKey = `${repo}-pr-graphql-snapshot`;
3394
- const gqlResp = await cacheFetch(gqlCacheKey, () => octokitGraphql(`
3266
+ const gqlResp = await utils.cacheFetch(gqlCacheKey, () => octokitGraphql(`
3395
3267
  query($owner: String!, $repo: String!, $states: [PullRequestState!]) {
3396
3268
  repository(owner: $owner, name: $repo) {
3397
3269
  pullRequests(first: 50, states: $states, orderBy: {field: CREATED_AT, direction: DESC}) {
@@ -3448,7 +3320,7 @@ async function getSocketPrsWithContext(owner, repo, options) {
3448
3320
  let allPrs;
3449
3321
  const cacheKey = `${repo}-pull-requests`;
3450
3322
  try {
3451
- allPrs = await cacheFetch(cacheKey, async () => await octokit.paginate(octokit.pulls.list, {
3323
+ allPrs = await utils.cacheFetch(cacheKey, async () => await octokit.paginate(octokit.pulls.list, {
3452
3324
  owner,
3453
3325
  repo,
3454
3326
  state: 'all',
@@ -3497,83 +3369,6 @@ async function getSocketPrsWithContext(owner, repo, options) {
3497
3369
  }
3498
3370
  return contextualMatches;
3499
3371
  }
3500
- async function openCoanaPr(owner, repo, branch, ghsaIds, options) {
3501
- const {
3502
- baseBranch = 'main',
3503
- ghsaDetails
3504
- } = {
3505
- __proto__: null,
3506
- ...options
3507
- };
3508
- const octokit = getOctokit();
3509
- const vulnCount = ghsaIds.length;
3510
- const prTitle = vulnCount === 1 ? `Fix for ${ghsaIds[0]}` : `Fixes for ${vulnCount} GHSAs`;
3511
- let prBody = '';
3512
- if (vulnCount === 1) {
3513
- const ghsaId = ghsaIds[0];
3514
- const details = ghsaDetails?.get(ghsaId);
3515
- prBody = `[Socket](https://socket.dev/) fix for [${ghsaId}](https://github.com/advisories/${ghsaId}).`;
3516
- if (details) {
3517
- const packages = details.vulnerabilities.nodes.map(v => `${v.package.name} (${v.package.ecosystem})`);
3518
- prBody += ['', '', `**Vulnerability Summary:** ${details.summary}`, '', `**Severity:** ${details.severity}`, '', `**Affected Packages:** ${arrays.joinAnd(packages)}`].join('\n');
3519
- }
3520
- } else {
3521
- prBody = [`[Socket](https://socket.dev/) fixes for ${vulnCount} GHSAs.`, '', '**Fixed Vulnerabilities:**', ...ghsaIds.map(id => {
3522
- const details = ghsaDetails?.get(id);
3523
- const item = `- [${id}](https://github.com/advisories/${id})`;
3524
- if (details) {
3525
- const packages = details.vulnerabilities.nodes.map(v => `${v.package.name}`);
3526
- return `${item} - ${details.summary} (${arrays.joinAnd(packages)})`;
3527
- }
3528
- return item;
3529
- })].join('\n');
3530
- }
3531
- try {
3532
- const octokitPullsCreateParams = {
3533
- owner,
3534
- repo,
3535
- title: prTitle,
3536
- head: branch,
3537
- base: baseBranch,
3538
- body: prBody
3539
- };
3540
- require$$9.debugDir('inspect', {
3541
- octokitPullsCreateParams
3542
- });
3543
- return await octokit.pulls.create(octokitPullsCreateParams);
3544
- } catch (e) {
3545
- let message = `Failed to open pull request`;
3546
- const errors = e instanceof vendor.RequestError ? e.response?.data?.['errors'] : undefined;
3547
- if (Array.isArray(errors) && errors.length) {
3548
- const details = errors.map(d => `- ${d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`}`).join('\n');
3549
- message += `:\n${details}`;
3550
- }
3551
- require$$9.debugFn('error', message);
3552
- }
3553
- return null;
3554
- }
3555
- async function setGitRemoteGithubRepoUrl(owner, repo, token, cwd = process.cwd()) {
3556
- const {
3557
- host
3558
- } = new URL(constants.ENV.GITHUB_SERVER_URL);
3559
- const url = `https://x-access-token:${token}@${host}/${owner}/${repo}`;
3560
- const stdioIgnoreOptions = {
3561
- cwd,
3562
- stdio: require$$9.isDebug('stdio') ? 'inherit' : 'ignore'
3563
- };
3564
- const quotedCmd = `\`git remote set-url origin ${url}\``;
3565
- require$$9.debugFn('stdio', `spawn: ${quotedCmd}`);
3566
- try {
3567
- await spawn.spawn('git', ['remote', 'set-url', 'origin', url], stdioIgnoreOptions);
3568
- return true;
3569
- } catch (e) {
3570
- require$$9.debugFn('error', `caught: ${quotedCmd} failed`);
3571
- require$$9.debugDir('inspect', {
3572
- error: e
3573
- });
3574
- }
3575
- return false;
3576
- }
3577
3372
 
3578
3373
  function ciRepoInfo() {
3579
3374
  const {
@@ -3652,7 +3447,9 @@ async function coanaFix(fixConfig) {
3652
3447
  return sockSdkCResult;
3653
3448
  }
3654
3449
  const sockSdk = sockSdkCResult.data;
3655
- const supportedFilesCResult = await fetchSupportedScanFileNames();
3450
+ const supportedFilesCResult = await fetchSupportedScanFileNames({
3451
+ spinner
3452
+ });
3656
3453
  if (!supportedFilesCResult.ok) {
3657
3454
  return supportedFilesCResult;
3658
3455
  }
@@ -3731,7 +3528,7 @@ async function coanaFix(fixConfig) {
3731
3528
  };
3732
3529
  }
3733
3530
  require$$9.debugFn('notice', `fetch: ${ids.length} GHSA details for ${arrays.joinAnd(ids)}`);
3734
- const ghsaDetails = await fetchGhsaDetails(ids);
3531
+ const ghsaDetails = await utils.fetchGhsaDetails(ids);
3735
3532
  const scanBaseNames = new Set(scanFilepaths.map(p => path.basename(p)));
3736
3533
  require$$9.debugFn('notice', `found: ${ghsaDetails.size} GHSA details`);
3737
3534
  let count = 0;
@@ -3741,18 +3538,18 @@ async function coanaFix(fixConfig) {
3741
3538
  ghsaLoop: for (let i = 0, {
3742
3539
  length
3743
3540
  } = ids; i < length; i += 1) {
3744
- const id = ids[i];
3745
- require$$9.debugFn('notice', `check: ${id}`);
3541
+ const ghsaId = ids[i];
3542
+ require$$9.debugFn('notice', `check: ${ghsaId}`);
3746
3543
 
3747
3544
  // Apply fix for single GHSA ID.
3748
3545
  // eslint-disable-next-line no-await-in-loop
3749
- const fixCResult = await utils.spawnCoana(['compute-fixes-and-upgrade-purls', cwd, '--manifests-tar-hash', tarHash, '--apply-fixes-to', id, ...(fixConfig.rangeStyle ? ['--range-style', fixConfig.rangeStyle] : []), ...fixConfig.unknownFlags], fixConfig.orgSlug, {
3546
+ const fixCResult = await utils.spawnCoana(['compute-fixes-and-upgrade-purls', cwd, '--manifests-tar-hash', tarHash, '--apply-fixes-to', ghsaId, ...(fixConfig.rangeStyle ? ['--range-style', fixConfig.rangeStyle] : []), ...fixConfig.unknownFlags], fixConfig.orgSlug, {
3750
3547
  cwd,
3751
3548
  spinner,
3752
3549
  stdio: 'inherit'
3753
3550
  });
3754
3551
  if (!fixCResult.ok) {
3755
- logger.logger.error(`Update failed for ${id}: ${fixCResult.message || 'Unknown error'}`);
3552
+ logger.logger.error(`Update failed for ${ghsaId}: ${fixCResult.message || 'Unknown error'}`);
3756
3553
  continue ghsaLoop;
3757
3554
  }
3758
3555
 
@@ -3761,11 +3558,11 @@ async function coanaFix(fixConfig) {
3761
3558
  const unstagedCResult = await utils.gitUnstagedModifiedFiles(cwd);
3762
3559
  const modifiedFiles = unstagedCResult.ok ? unstagedCResult.data.filter(relPath => scanBaseNames.has(path.basename(relPath))) : [];
3763
3560
  if (!modifiedFiles.length) {
3764
- require$$9.debugFn('notice', `skip: no changes for ${id}`);
3561
+ require$$9.debugFn('notice', `skip: no changes for ${ghsaId}`);
3765
3562
  continue ghsaLoop;
3766
3563
  }
3767
3564
  overallFixed = true;
3768
- const branch = `socket/fix/${id}`;
3565
+ const branch = getSocketFixBranchName(ghsaId);
3769
3566
  try {
3770
3567
  // Check if branch already exists.
3771
3568
  // eslint-disable-next-line no-await-in-loop
@@ -3773,17 +3570,16 @@ async function coanaFix(fixConfig) {
3773
3570
  require$$9.debugFn('notice', `skip: remote branch "${branch}" exists`);
3774
3571
  continue ghsaLoop;
3775
3572
  }
3776
- require$$9.debugFn('notice', `pr: creating for ${id}`);
3777
- const details = ghsaDetails.get(id);
3778
- const summary = details?.summary;
3779
- require$$9.debugFn('notice', `ghsa: ${id} details ${details ? 'found' : 'missing'}`);
3573
+ require$$9.debugFn('notice', `pr: creating for ${ghsaId}`);
3574
+ const details = ghsaDetails.get(ghsaId);
3575
+ require$$9.debugFn('notice', `ghsa: ${ghsaId} details ${details ? 'found' : 'missing'}`);
3780
3576
  const pushed =
3781
3577
  // eslint-disable-next-line no-await-in-loop
3782
3578
  (await utils.gitCreateBranch(branch, cwd)) && (
3783
3579
  // eslint-disable-next-line no-await-in-loop
3784
3580
  await utils.gitCheckoutBranch(branch, cwd)) && (
3785
3581
  // eslint-disable-next-line no-await-in-loop
3786
- await utils.gitCommit(`fix: ${id}${summary ? ` - ${summary}` : ''}`, modifiedFiles, {
3582
+ await utils.gitCommit(getSocketFixCommitMessage(ghsaId, details), modifiedFiles, {
3787
3583
  cwd,
3788
3584
  email: fixEnv.gitEmail,
3789
3585
  user: fixEnv.gitUser
@@ -3791,7 +3587,7 @@ async function coanaFix(fixConfig) {
3791
3587
  // eslint-disable-next-line no-await-in-loop
3792
3588
  await utils.gitPushBranch(branch, cwd));
3793
3589
  if (!pushed) {
3794
- logger.logger.warn(`Push failed for ${id}, skipping PR creation.`);
3590
+ logger.logger.warn(`Push failed for ${ghsaId}, skipping PR creation.`);
3795
3591
  // eslint-disable-next-line no-await-in-loop
3796
3592
  await utils.gitResetAndClean(fixEnv.baseBranch, cwd);
3797
3593
  // eslint-disable-next-line no-await-in-loop
@@ -3803,12 +3599,12 @@ async function coanaFix(fixConfig) {
3803
3599
 
3804
3600
  // Set up git remote.
3805
3601
  // eslint-disable-next-line no-await-in-loop
3806
- await setGitRemoteGithubRepoUrl(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, fixEnv.githubToken, cwd);
3602
+ await utils.setGitRemoteGithubRepoUrl(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, fixEnv.githubToken, cwd);
3807
3603
 
3808
3604
  // eslint-disable-next-line no-await-in-loop
3809
- const prResponse = await openCoanaPr(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, branch,
3605
+ const prResponse = await openSocketFixPr(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, branch,
3810
3606
  // Single GHSA ID.
3811
- [id], {
3607
+ [ghsaId], {
3812
3608
  baseBranch: fixEnv.baseBranch,
3813
3609
  cwd,
3814
3610
  ghsaDetails
@@ -3818,7 +3614,7 @@ async function coanaFix(fixConfig) {
3818
3614
  data
3819
3615
  } = prResponse;
3820
3616
  const prRef = `PR #${data.number}`;
3821
- logger.logger.success(`Opened ${prRef} for ${id}.`);
3617
+ logger.logger.success(`Opened ${prRef} for ${ghsaId}.`);
3822
3618
  if (autoMerge) {
3823
3619
  logger.logger.indent();
3824
3620
  spinner?.indent();
@@ -3826,7 +3622,7 @@ async function coanaFix(fixConfig) {
3826
3622
  const {
3827
3623
  details,
3828
3624
  enabled
3829
- } = await enablePrAutoMerge(data);
3625
+ } = await utils.enablePrAutoMerge(data);
3830
3626
  if (enabled) {
3831
3627
  logger.logger.info(`Auto-merge enabled for ${prRef}.`);
3832
3628
  } else {
@@ -3844,7 +3640,7 @@ async function coanaFix(fixConfig) {
3844
3640
  // eslint-disable-next-line no-await-in-loop
3845
3641
  await utils.gitCheckoutBranch(fixEnv.baseBranch, cwd);
3846
3642
  } catch (e) {
3847
- logger.logger.warn(`Unexpected condition: Push failed for ${id}, skipping PR creation.`);
3643
+ logger.logger.warn(`Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`);
3848
3644
  require$$9.debugDir('inspect', {
3849
3645
  error: e
3850
3646
  });
@@ -3921,69 +3717,27 @@ const cmdFix = {
3921
3717
  hidden: hidden$q,
3922
3718
  run: run$I
3923
3719
  };
3924
- async function run$I(argv, importMeta, {
3925
- parentName
3926
- }) {
3927
- const config = {
3928
- commandName: CMD_NAME$r,
3929
- description: description$x,
3930
- hidden: hidden$q,
3931
- flags: {
3932
- ...flags.commonFlags,
3933
- ...flags.outputFlags,
3934
- autoMerge: {
3935
- type: 'boolean',
3936
- default: false,
3937
- description: `Enable auto-merge for pull requests that Socket opens.\nSee ${vendor.terminalLinkExports('GitHub documentation', 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-auto-merge-for-pull-requests-in-your-repository')} for managing auto-merge for pull requests in your repository.`
3938
- },
3939
- autopilot: {
3940
- type: 'boolean',
3941
- default: false,
3942
- description: `Shorthand for --auto-merge --test`,
3943
- hidden: true
3944
- },
3945
- ghsa: {
3946
- type: 'string',
3947
- default: [],
3948
- description: `Provide a list of ${vendor.terminalLinkExports('GHSA IDs', 'https://docs.github.com/en/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/about-the-github-advisory-database#about-ghsa-ids')} to compute fixes for, as either a comma separated value or as multiple flags.\nUse '--ghsa all' to lookup all GHSA IDs and compute fixes for them.`,
3949
- isMultiple: true,
3950
- hidden: true
3951
- },
3952
- limit: {
3953
- type: 'number',
3954
- default: DEFAULT_LIMIT,
3955
- description: `The number of fixes to attempt at a time (default ${DEFAULT_LIMIT})`
3956
- },
3957
- maxSatisfying: {
3958
- type: 'boolean',
3959
- default: true,
3960
- description: 'Use the maximum satisfying version for dependency updates',
3961
- hidden: true
3962
- },
3963
- minSatisfying: {
3964
- type: 'boolean',
3965
- default: false,
3966
- description: 'Constrain dependency updates to the minimum satisfying version',
3967
- hidden: true
3968
- },
3969
- prCheck: {
3970
- type: 'boolean',
3971
- default: true,
3972
- description: 'Check for an existing PR before attempting a fix',
3973
- hidden: true
3974
- },
3975
- purl: {
3976
- type: 'string',
3977
- default: [],
3978
- description: `Provide a list of ${vendor.terminalLinkExports('PURLs', 'https://github.com/package-url/purl-spec?tab=readme-ov-file#purl')} to compute fixes for, as either a comma separated value or as\nmultiple flags, instead of querying the Socket API`,
3979
- isMultiple: true,
3980
- shortFlag: 'p',
3981
- hidden: true
3982
- },
3983
- rangeStyle: {
3984
- type: 'string',
3985
- default: 'preserve',
3986
- description: `
3720
+ const generalFlags$2 = {
3721
+ autoMerge: {
3722
+ type: 'boolean',
3723
+ default: false,
3724
+ description: `Enable auto-merge for pull requests that Socket opens.\nSee ${vendor.terminalLinkExports('GitHub documentation', 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-auto-merge-for-pull-requests-in-your-repository')} for managing auto-merge for pull requests in your repository.`
3725
+ },
3726
+ id: {
3727
+ type: 'string',
3728
+ default: [],
3729
+ description: `Provide a list of ${vendor.terminalLinkExports('GHSA IDs', 'https://docs.github.com/en/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/about-the-github-advisory-database#about-ghsa-ids')} to compute fixes for, as either a comma separated value or as multiple flags`,
3730
+ isMultiple: true
3731
+ },
3732
+ limit: {
3733
+ type: 'number',
3734
+ default: DEFAULT_LIMIT,
3735
+ description: `The number of fixes to attempt at a time (default ${DEFAULT_LIMIT})`
3736
+ },
3737
+ rangeStyle: {
3738
+ type: 'string',
3739
+ default: 'preserve',
3740
+ description: `
3987
3741
  Define how dependency version ranges are updated in package.json (default 'preserve').
3988
3742
  Available styles:
3989
3743
  * caret - Use ^ range for compatible updates (e.g. ^1.2.3)
@@ -3995,17 +3749,70 @@ Available styles:
3995
3749
  * preserve - Retain the existing version range style as-is
3996
3750
  * tilde - Use ~ range for patch/minor updates (e.g. ~1.2.3)
3997
3751
  `.trim()
3998
- },
3999
- test: {
4000
- type: 'boolean',
4001
- default: false,
4002
- description: 'Verify the fix by running unit tests'
4003
- },
4004
- testScript: {
4005
- type: 'string',
4006
- default: 'test',
4007
- description: "The test script to run for fix attempts (default 'test')"
4008
- }
3752
+ }
3753
+ };
3754
+ const hiddenFlags = {
3755
+ autopilot: {
3756
+ type: 'boolean',
3757
+ default: false,
3758
+ description: `Shorthand for --auto-merge --test`,
3759
+ hidden: true
3760
+ },
3761
+ ghsa: {
3762
+ ...generalFlags$2['id'],
3763
+ hidden: true
3764
+ },
3765
+ maxSatisfying: {
3766
+ type: 'boolean',
3767
+ default: true,
3768
+ description: 'Use the maximum satisfying version for dependency updates',
3769
+ hidden: true
3770
+ },
3771
+ minSatisfying: {
3772
+ type: 'boolean',
3773
+ default: false,
3774
+ description: 'Constrain dependency updates to the minimum satisfying version',
3775
+ hidden: true
3776
+ },
3777
+ prCheck: {
3778
+ type: 'boolean',
3779
+ default: true,
3780
+ description: 'Check for an existing PR before attempting a fix',
3781
+ hidden: true
3782
+ },
3783
+ purl: {
3784
+ type: 'string',
3785
+ default: [],
3786
+ description: `Provide a list of ${vendor.terminalLinkExports('PURLs', 'https://github.com/package-url/purl-spec?tab=readme-ov-file#purl')} to compute fixes for, as either a comma separated value or as\nmultiple flags, instead of querying the Socket API`,
3787
+ isMultiple: true,
3788
+ shortFlag: 'p',
3789
+ hidden: true
3790
+ },
3791
+ test: {
3792
+ type: 'boolean',
3793
+ default: false,
3794
+ description: 'Verify the fix by running unit tests',
3795
+ hidden: true
3796
+ },
3797
+ testScript: {
3798
+ type: 'string',
3799
+ default: 'test',
3800
+ description: "The test script to run for fix attempts (default 'test')",
3801
+ hidden: true
3802
+ }
3803
+ };
3804
+ async function run$I(argv, importMeta, {
3805
+ parentName
3806
+ }) {
3807
+ const config = {
3808
+ commandName: CMD_NAME$r,
3809
+ description: description$x,
3810
+ hidden: hidden$q,
3811
+ flags: {
3812
+ ...flags.commonFlags,
3813
+ ...flags.outputFlags,
3814
+ ...generalFlags$2,
3815
+ ...hiddenFlags
4009
3816
  },
4010
3817
  help: (command, config) => `
4011
3818
  Usage
@@ -4093,7 +3900,7 @@ Available styles:
4093
3900
  // We patched in this feature with `npx custompatch meow` at
4094
3901
  // socket-cli/patches/meow#13.2.0.patch.
4095
3902
  const unknownFlags = cli.unknownFlags ?? [];
4096
- const ghsas = utils.cmdFlagValueToArray(cli.flags['ghsa']);
3903
+ const ghsas = arrays.arrayUnique([...utils.cmdFlagValueToArray(cli.flags['id']), ...utils.cmdFlagValueToArray(cli.flags['ghsa'])]);
4097
3904
  const limit = Number(cli.flags['limit']) || DEFAULT_LIMIT;
4098
3905
  const maxSatisfying = Boolean(cli.flags['maxSatisfying']);
4099
3906
  const minSatisfying = Boolean(cli.flags['minSatisfying']) || !maxSatisfying;
@@ -6847,8 +6654,8 @@ function updatePkgJsonField(editablePkgJson, field, value) {
6847
6654
  if (oldValue) {
6848
6655
  // The field already exists so we simply update the field value.
6849
6656
  if (field === PNPM) {
6850
- const isPnpmObj = require$$10.isObject(oldValue);
6851
- if (require$$10.hasKeys(value)) {
6657
+ const isPnpmObj = require$$11.isObject(oldValue);
6658
+ if (require$$11.hasKeys(value)) {
6852
6659
  editablePkgJson.update({
6853
6660
  [field]: {
6854
6661
  ...(isPnpmObj ? oldValue : {}),
@@ -6860,7 +6667,7 @@ function updatePkgJsonField(editablePkgJson, field, value) {
6860
6667
  });
6861
6668
  } else {
6862
6669
  // Properties with undefined values are deleted when saved as JSON.
6863
- editablePkgJson.update(require$$10.hasKeys(oldValue) ? {
6670
+ editablePkgJson.update(require$$11.hasKeys(oldValue) ? {
6864
6671
  [field]: {
6865
6672
  ...(isPnpmObj ? oldValue : {}),
6866
6673
  overrides: undefined
@@ -6872,7 +6679,7 @@ function updatePkgJsonField(editablePkgJson, field, value) {
6872
6679
  } else if (field === OVERRIDES || field === RESOLUTIONS) {
6873
6680
  // Properties with undefined values are deleted when saved as JSON.
6874
6681
  editablePkgJson.update({
6875
- [field]: require$$10.hasKeys(value) ? value : undefined
6682
+ [field]: require$$11.hasKeys(value) ? value : undefined
6876
6683
  });
6877
6684
  } else {
6878
6685
  editablePkgJson.update({
@@ -6881,7 +6688,7 @@ function updatePkgJsonField(editablePkgJson, field, value) {
6881
6688
  }
6882
6689
  return;
6883
6690
  }
6884
- if ((field === OVERRIDES || field === PNPM || field === RESOLUTIONS) && !require$$10.hasKeys(value)) {
6691
+ if ((field === OVERRIDES || field === PNPM || field === RESOLUTIONS) && !require$$11.hasKeys(value)) {
6885
6692
  return;
6886
6693
  }
6887
6694
  // Since the field doesn't exist we want to insert it into the package.json
@@ -7013,7 +6820,7 @@ async function addOverrides(pkgEnvDetails, pkgPath, options) {
7013
6820
  let loggedAddingText = false;
7014
6821
 
7015
6822
  // Chunk package names to process them in parallel 3 at a time.
7016
- await require$$11.pEach(manifestEntries, async ({
6823
+ await require$$12.pEach(manifestEntries, async ({
7017
6824
  1: data
7018
6825
  }) => {
7019
6826
  const {
@@ -7027,11 +6834,11 @@ async function addOverrides(pkgEnvDetails, pkgPath, options) {
7027
6834
  for (const {
7028
6835
  1: depObj
7029
6836
  } of depEntries) {
7030
- const sockSpec = require$$10.hasOwn(depObj, sockRegPkgName) ? depObj[sockRegPkgName] : undefined;
6837
+ const sockSpec = require$$11.hasOwn(depObj, sockRegPkgName) ? depObj[sockRegPkgName] : undefined;
7031
6838
  if (sockSpec) {
7032
6839
  depAliasMap.set(sockRegPkgName, sockSpec);
7033
6840
  }
7034
- const origSpec = require$$10.hasOwn(depObj, origPkgName) ? depObj[origPkgName] : undefined;
6841
+ const origSpec = require$$11.hasOwn(depObj, origPkgName) ? depObj[origPkgName] : undefined;
7035
6842
  if (origSpec) {
7036
6843
  let thisSpec = origSpec;
7037
6844
  // Add package aliases for direct dependencies to avoid npm EOVERRIDE
@@ -7067,11 +6874,11 @@ async function addOverrides(pkgEnvDetails, pkgPath, options) {
7067
6874
  npmExecPath
7068
6875
  });
7069
6876
  // Chunk package names to process them in parallel 3 at a time.
7070
- await require$$11.pEach(overridesDataObjects, async ({
6877
+ await require$$12.pEach(overridesDataObjects, async ({
7071
6878
  overrides,
7072
6879
  type
7073
6880
  }) => {
7074
- const overrideExists = require$$10.hasOwn(overrides, origPkgName);
6881
+ const overrideExists = require$$11.hasOwn(overrides, origPkgName);
7075
6882
  if (overrideExists || thingScanner(pkgEnvDetails, thingToScan, origPkgName, lockName)) {
7076
6883
  const oldSpec = overrideExists ? overrides[origPkgName] : undefined;
7077
6884
  const origDepAlias = depAliasMap.get(origPkgName);
@@ -7125,7 +6932,7 @@ async function addOverrides(pkgEnvDetails, pkgPath, options) {
7125
6932
  });
7126
6933
  if (isWorkspace) {
7127
6934
  // Chunk package names to process them in parallel 3 at a time.
7128
- await require$$11.pEach(workspacePkgJsonPaths, async workspacePkgJsonPath => {
6935
+ await require$$12.pEach(workspacePkgJsonPaths, async workspacePkgJsonPath => {
7129
6936
  const otherState = await addOverrides(pkgEnvDetails, path.dirname(workspacePkgJsonPath), {
7130
6937
  logger,
7131
6938
  pin,
@@ -7148,7 +6955,7 @@ async function addOverrides(pkgEnvDetails, pkgPath, options) {
7148
6955
  overrides,
7149
6956
  type
7150
6957
  } of overridesDataObjects) {
7151
- updateManifest(type, pkgEnvDetails.editablePkgJson, require$$10.toSortedObject(overrides));
6958
+ updateManifest(type, pkgEnvDetails.editablePkgJson, require$$11.toSortedObject(overrides));
7152
6959
  }
7153
6960
  }
7154
6961
  await pkgEnvDetails.editablePkgJson.save();
@@ -8866,6 +8673,30 @@ const cmdPackage = {
8866
8673
  }
8867
8674
  };
8868
8675
 
8676
+ const PatchRecordSchema = vendor.object({
8677
+ exportedAt: vendor.string(),
8678
+ files: vendor.record(vendor.string(),
8679
+ // File path
8680
+ vendor.object({
8681
+ beforeHash: vendor.string(),
8682
+ afterHash: vendor.string()
8683
+ })),
8684
+ vulnerabilities: vendor.record(vendor.string(),
8685
+ // Vulnerability ID like "GHSA-jrhj-2j3q-xf3v"
8686
+ vendor.object({
8687
+ cves: vendor.array(vendor.string()),
8688
+ summary: vendor.string(),
8689
+ severity: vendor.string(),
8690
+ description: vendor.string(),
8691
+ patchExplanation: vendor.string()
8692
+ }))
8693
+ });
8694
+ const PatchManifestSchema = vendor.object({
8695
+ patches: vendor.record(
8696
+ // Package identifier like "npm:simplehttpserver@0.0.6".
8697
+ vendor.string(), PatchRecordSchema)
8698
+ });
8699
+
8869
8700
  async function outputPatchResult(result, outputKind) {
8870
8701
  if (!result.ok) {
8871
8702
  process.exitCode = result.code ?? 1;
@@ -8893,21 +8724,220 @@ async function outputPatchResult(result, outputKind) {
8893
8724
  logger.logger.success('Patch command completed!');
8894
8725
  }
8895
8726
 
8727
+ async function applyNPMPatches(patches, dryRun, socketDir, packages) {
8728
+ const patchLookup = new Map();
8729
+ for (const patchInfo of patches) {
8730
+ const {
8731
+ purl
8732
+ } = patchInfo;
8733
+ const fullName = purl.namespace ? `@${purl.namespace}/${purl.name}` : purl.name;
8734
+ const lookupKey = `${fullName}@${purl.version}`;
8735
+ patchLookup.set(lookupKey, patchInfo);
8736
+ }
8737
+ const nodeModulesFolders = await findNodeModulesFolders(process.cwd());
8738
+ logger.logger.log(`Found ${nodeModulesFolders.length} node_modules folders`);
8739
+ for (const nodeModulesPath of nodeModulesFolders) {
8740
+ try {
8741
+ // eslint-disable-next-line no-await-in-loop
8742
+ const entries = await fs$1.promises.readdir(nodeModulesPath);
8743
+ for (const entry of entries) {
8744
+ const entryPath = path.join(nodeModulesPath, entry);
8745
+ if (entry.startsWith('@')) {
8746
+ try {
8747
+ // eslint-disable-next-line no-await-in-loop
8748
+ const scopedEntries = await fs$1.promises.readdir(entryPath);
8749
+ for (const scopedEntry of scopedEntries) {
8750
+ const packagePath = path.join(entryPath, scopedEntry);
8751
+ // eslint-disable-next-line no-await-in-loop
8752
+ const pkg = await readPackageJson(packagePath);
8753
+ if (pkg) {
8754
+ // Skip if specific packages requested and this isn't one of them
8755
+ if (packages.length > 0 && !packages.includes(pkg.name)) {
8756
+ continue;
8757
+ }
8758
+ const lookupKey = `${pkg.name}@${pkg.version}`;
8759
+ const patchInfo = patchLookup.get(lookupKey);
8760
+ if (patchInfo) {
8761
+ logger.logger.log(`Found match: ${pkg.name}@${pkg.version} at ${packagePath}`);
8762
+ logger.logger.log(` Patch key: ${patchInfo.key}`);
8763
+ logger.logger.log(` Processing files:`);
8764
+ for (const [fileName, fileInfo] of Object.entries(patchInfo.patch.files)) {
8765
+ // eslint-disable-next-line no-await-in-loop
8766
+ await processFilePatch(packagePath, fileName, fileInfo, dryRun, socketDir);
8767
+ }
8768
+ }
8769
+ }
8770
+ }
8771
+ } catch {
8772
+ // Ignore errors reading scoped packages
8773
+ }
8774
+ } else {
8775
+ // eslint-disable-next-line no-await-in-loop
8776
+ const pkg = await readPackageJson(entryPath);
8777
+ if (pkg) {
8778
+ // Skip if specific packages requested and this isn't one of them
8779
+ if (packages.length > 0 && !packages.includes(pkg.name)) {
8780
+ continue;
8781
+ }
8782
+ const lookupKey = `${pkg.name}@${pkg.version}`;
8783
+ const patchInfo = patchLookup.get(lookupKey);
8784
+ if (patchInfo) {
8785
+ logger.logger.log(`Found match: ${pkg.name}@${pkg.version} at ${entryPath}`);
8786
+ logger.logger.log(` Patch key: ${patchInfo.key}`);
8787
+ logger.logger.log(` Processing files:`);
8788
+ for (const [fileName, fileInfo] of Object.entries(patchInfo.patch.files)) {
8789
+ // eslint-disable-next-line no-await-in-loop
8790
+ await processFilePatch(entryPath, fileName, fileInfo, dryRun, socketDir);
8791
+ }
8792
+ }
8793
+ }
8794
+ }
8795
+ }
8796
+ } catch (error) {
8797
+ logger.logger.error(`Error processing ${nodeModulesPath}:`, error);
8798
+ }
8799
+ }
8800
+ }
8801
+ async function computeSHA256(filePath) {
8802
+ try {
8803
+ const content = await fs$1.promises.readFile(filePath);
8804
+ const hash = require$$0$1.createHash('sha256');
8805
+ hash.update(content);
8806
+ return hash.digest('hex');
8807
+ } catch {
8808
+ return null;
8809
+ }
8810
+ }
8811
+ async function findNodeModulesFolders(rootDir) {
8812
+ const nodeModulesPaths = [];
8813
+ async function searchDir(dir) {
8814
+ try {
8815
+ const entries = await fs$1.promises.readdir(dir);
8816
+ for (const entry of entries) {
8817
+ if (entry.startsWith('.') || entry === 'dist' || entry === 'build') {
8818
+ continue;
8819
+ }
8820
+ const fullPath = path.join(dir, entry);
8821
+ // eslint-disable-next-line no-await-in-loop
8822
+ const stats = await fs$1.promises.stat(fullPath);
8823
+ if (stats.isDirectory()) {
8824
+ if (entry === 'node_modules') {
8825
+ nodeModulesPaths.push(fullPath);
8826
+ } else {
8827
+ // eslint-disable-next-line no-await-in-loop
8828
+ await searchDir(fullPath);
8829
+ }
8830
+ }
8831
+ }
8832
+ } catch (error) {
8833
+ // Ignore permission errors or missing directories
8834
+ }
8835
+ }
8836
+ await searchDir(rootDir);
8837
+ return nodeModulesPaths;
8838
+ }
8839
+ function parsePURL(purlString) {
8840
+ const [ecosystem, rest] = purlString.split(':', 2);
8841
+ const [nameAndNamespace, version] = (rest ?? '').split('@', 2);
8842
+ let namespace;
8843
+ let name;
8844
+ if (ecosystem === 'npm' && nameAndNamespace?.startsWith('@')) {
8845
+ const parts = nameAndNamespace.split('/');
8846
+ namespace = parts[0]?.substring(1);
8847
+ name = parts.slice(1).join('/');
8848
+ } else {
8849
+ name = nameAndNamespace ?? '';
8850
+ }
8851
+ return {
8852
+ type: ecosystem ?? 'unknown',
8853
+ namespace: namespace ?? '',
8854
+ name: name ?? '',
8855
+ version: version ?? '0.0.0'
8856
+ };
8857
+ }
8858
+ async function processFilePatch(packagePath, fileName, fileInfo, dryRun, socketDir) {
8859
+ const filePath = path.join(packagePath, fileName);
8860
+ if (!fs$1.existsSync(filePath)) {
8861
+ logger.logger.log(`File not found: ${fileName}`);
8862
+ return;
8863
+ }
8864
+ const currentHash = await computeSHA256(filePath);
8865
+ if (!currentHash) {
8866
+ logger.logger.log(`Failed to compute hash for: ${fileName}`);
8867
+ return;
8868
+ }
8869
+ if (currentHash === fileInfo.beforeHash) {
8870
+ logger.logger.success(`File matches expected hash: ${fileName}`);
8871
+ logger.logger.log(`Current hash: ${currentHash}`);
8872
+ logger.logger.log(`Ready to patch to: ${fileInfo.afterHash}`);
8873
+ {
8874
+ const blobPath = path.join(socketDir, 'blobs', fileInfo.afterHash);
8875
+ if (!fs$1.existsSync(blobPath)) {
8876
+ logger.logger.fail(`Error: Patch file not found at ${blobPath}`);
8877
+ return;
8878
+ }
8879
+ try {
8880
+ await fs$1.promises.copyFile(blobPath, filePath);
8881
+ logger.logger.success(`Patch applied successfully`);
8882
+ } catch (error) {
8883
+ logger.logger.log(`Error applying patch: ${error}`);
8884
+ }
8885
+ }
8886
+ } else if (currentHash === fileInfo.afterHash) {
8887
+ logger.logger.success(`File already patched: ${fileName}`);
8888
+ logger.logger.log(`Current hash: ${currentHash}`);
8889
+ } else {
8890
+ logger.logger.fail(`File hash mismatch: ${fileName}`);
8891
+ logger.logger.log(`Expected: ${fileInfo.beforeHash}`);
8892
+ logger.logger.log(`Current: ${currentHash}`);
8893
+ logger.logger.log(`Target: ${fileInfo.afterHash}`);
8894
+ }
8895
+ }
8896
+ async function readPackageJson(packagePath) {
8897
+ const pkgJsonPath = path.join(packagePath, 'package.json');
8898
+ const pkg = await fs$2.readJson(pkgJsonPath, {
8899
+ throws: false
8900
+ });
8901
+ if (pkg) {
8902
+ return {
8903
+ name: pkg.name || '',
8904
+ version: pkg.version || ''
8905
+ };
8906
+ }
8907
+ return null;
8908
+ }
8896
8909
  async function handlePatch({
8910
+ cwd,
8897
8911
  outputKind,
8898
8912
  packages,
8899
8913
  spinner
8900
8914
  }) {
8901
- spinner.start('Analyzing dependencies for security patches...');
8915
+ const dryRun = false; // TODO: Add dryRun support via config
8916
+
8902
8917
  try {
8903
- // TODO: Implement actual patch logic
8904
- // This is a stub implementation
8905
- const result = {
8906
- ok: true,
8907
- data: {
8908
- patchedPackages: packages.length > 0 ? packages : ['example-package']
8918
+ const dotSocketDirPath = path.join(cwd, '.socket');
8919
+ const manifestPath = path.join(dotSocketDirPath, 'manifest.json');
8920
+
8921
+ // Read the manifest file
8922
+ const manifestContent = await fs$1.promises.readFile(manifestPath, 'utf-8');
8923
+ const manifestData = JSON.parse(manifestContent);
8924
+
8925
+ // Validate the schema
8926
+ const validated = PatchManifestSchema.parse(manifestData);
8927
+
8928
+ // Parse PURLs and group by ecosystem
8929
+ const patchesByEcosystem = {};
8930
+ for (const [key, patch] of Object.entries(validated.patches)) {
8931
+ const purl = parsePURL(key);
8932
+ if (!patchesByEcosystem[purl.type]) {
8933
+ patchesByEcosystem[purl.type] = [];
8909
8934
  }
8910
- };
8935
+ patchesByEcosystem[purl.type]?.push({
8936
+ key,
8937
+ purl,
8938
+ patch
8939
+ });
8940
+ }
8911
8941
  spinner.stop();
8912
8942
  logger.logger.log('');
8913
8943
  if (packages.length > 0) {
@@ -8916,14 +8946,32 @@ async function handlePatch({
8916
8946
  logger.logger.info('Scanning all dependencies for available patches');
8917
8947
  }
8918
8948
  logger.logger.log('');
8949
+ if (patchesByEcosystem['npm']) {
8950
+ await applyNPMPatches(patchesByEcosystem['npm'], dryRun, dotSocketDirPath, packages);
8951
+ }
8952
+ const result = {
8953
+ ok: true,
8954
+ data: {
8955
+ patchedPackages: packages.length > 0 ? packages : ['patched successfully']
8956
+ }
8957
+ };
8919
8958
  await outputPatchResult(result, outputKind);
8920
8959
  } catch (e) {
8921
8960
  spinner.stop();
8961
+ let message = 'Failed to apply patches';
8962
+ let cause = e?.message || 'Unknown error';
8963
+ if (e instanceof SyntaxError) {
8964
+ message = 'Invalid JSON in manifest.json';
8965
+ cause = e.message;
8966
+ } else if (e instanceof Error && 'issues' in e) {
8967
+ message = 'Schema validation failed';
8968
+ cause = String(e);
8969
+ }
8922
8970
  const result = {
8923
8971
  ok: false,
8924
8972
  code: 1,
8925
- message: 'Failed to apply patches',
8926
- cause: e?.message || 'Unknown error'
8973
+ message,
8974
+ cause
8927
8975
  };
8928
8976
  await outputPatchResult(result, outputKind);
8929
8977
  }
@@ -8997,11 +9045,21 @@ async function run$k(argv, importMeta, {
8997
9045
  // Note: path.resolve vs .join:
8998
9046
  // If given path is absolute then cwd should not affect it.
8999
9047
  cwd = path.resolve(process.cwd(), cwd);
9048
+ const dotSocketDirPath = path.join(cwd, '.socket');
9049
+ if (!fs$1.existsSync(dotSocketDirPath)) {
9050
+ logger.logger.error('Error: No .socket directory found in current directory');
9051
+ return;
9052
+ }
9053
+ const manifestPath = path.join(dotSocketDirPath, 'manifest.json');
9054
+ if (!fs$1.existsSync(manifestPath)) {
9055
+ logger.logger.error('Error: No manifest.json found in .socket directory');
9056
+ }
9000
9057
  const {
9001
9058
  spinner
9002
9059
  } = constants;
9003
- const packages = Array.isArray(cli.flags['package']) ? cli.flags['package'].flatMap(p => String(p).split(',')) : String(cli.flags['package'] || '').split(',').filter(Boolean);
9060
+ const packages = utils.cmdFlagValueToArray(cli.flags['package']);
9004
9061
  await handlePatch({
9062
+ cwd,
9005
9063
  outputKind,
9006
9064
  packages,
9007
9065
  spinner
@@ -12266,8 +12324,14 @@ async function handleScanReach({
12266
12324
  reachabilityOptions,
12267
12325
  targets
12268
12326
  }) {
12327
+ const {
12328
+ spinner
12329
+ } = constants;
12330
+
12269
12331
  // Get supported file names
12270
- const supportedFilesCResult = await fetchSupportedScanFileNames();
12332
+ const supportedFilesCResult = await fetchSupportedScanFileNames({
12333
+ spinner
12334
+ });
12271
12335
  if (!supportedFilesCResult.ok) {
12272
12336
  await outputScanReach(supportedFilesCResult, {
12273
12337
  cwd,
@@ -12275,9 +12339,6 @@ async function handleScanReach({
12275
12339
  });
12276
12340
  return;
12277
12341
  }
12278
- const {
12279
- spinner
12280
- } = constants;
12281
12342
  spinner.start('Searching for local manifest files to include in reachability analysis...');
12282
12343
  const supportedFiles = supportedFilesCResult.data;
12283
12344
  const packagePaths = await utils.getPackageFilesForScan(targets, supportedFiles, {
@@ -14203,5 +14264,5 @@ void (async () => {
14203
14264
  await utils.captureException(e);
14204
14265
  }
14205
14266
  })();
14206
- //# debugId=2d71faa1-844b-480a-a713-c572fd14e2f4
14267
+ //# debugId=41b305c0-5c97-4685-a7f1-88a4cbb5e41c
14207
14268
  //# sourceMappingURL=cli.js.map