@socketsecurity/cli 1.1.29 → 1.1.30

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/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [1.1.30](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.30) - 2025-11-18
8
+
9
+ ### Changed
10
+ - Enhanced `SOCKET_CLI_COANA_LOCAL_PATH` to support compiled Coana CLI binaries alongside Node.js script files
11
+
12
+ ### Fixed
13
+ - Resolved PR creation workflow to properly recreate pull requests after closing or merging
14
+ - Corrected API token selection to honor `SOCKET_CLI_API_TOKEN` environment variable in package alert requests
15
+
7
16
  ## [1.1.29](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.29) - 2025-11-16
8
17
 
9
18
  ### Added
package/dist/cli.js CHANGED
@@ -3264,6 +3264,68 @@ const cmdConfig = {
3264
3264
  }
3265
3265
  };
3266
3266
 
3267
+ /**
3268
+ * Branch cleanup utilities for socket fix command.
3269
+ * Manages local and remote branch lifecycle during PR creation.
3270
+ *
3271
+ * Critical distinction: Remote branches are sacred when a PR exists, disposable when they don't.
3272
+ */
3273
+
3274
+
3275
+ /**
3276
+ * Clean up a stale branch (both remote and local).
3277
+ * Safe to delete both since no PR exists for this branch.
3278
+ *
3279
+ * Returns true if cleanup succeeded or should continue, false if should skip GHSA.
3280
+ */
3281
+ async function cleanupStaleBranch(branch, ghsaId, cwd) {
3282
+ logger.logger.warn(`Stale branch ${branch} found without open PR, cleaning up...`);
3283
+ require$$9.debugFn('notice', `cleanup: deleting stale branch ${branch}`);
3284
+ const deleted = await utils.gitDeleteRemoteBranch(branch, cwd);
3285
+ if (!deleted) {
3286
+ logger.logger.error(`Failed to delete stale remote branch ${branch}, skipping ${ghsaId}.`);
3287
+ require$$9.debugFn('error', `cleanup: remote deletion failed for ${branch}`);
3288
+ return false;
3289
+ }
3290
+
3291
+ // Clean up local branch too to avoid conflicts.
3292
+ await utils.gitDeleteBranch(branch, cwd);
3293
+ return true;
3294
+ }
3295
+
3296
+ /**
3297
+ * Clean up branches after PR creation failure.
3298
+ * Safe to delete both remote and local since no PR was created.
3299
+ */
3300
+ async function cleanupFailedPrBranches(branch, cwd) {
3301
+ // Clean up pushed branch since PR creation failed.
3302
+ // Safe to delete both remote and local since no PR exists.
3303
+ await utils.gitDeleteRemoteBranch(branch, cwd);
3304
+ await utils.gitDeleteBranch(branch, cwd);
3305
+ }
3306
+
3307
+ /**
3308
+ * Clean up local branch after successful PR creation.
3309
+ * Keeps remote branch - PR needs it to be mergeable.
3310
+ */
3311
+ async function cleanupSuccessfulPrLocalBranch(branch, cwd) {
3312
+ // Clean up local branch only - keep remote branch for PR merge.
3313
+ await utils.gitDeleteBranch(branch, cwd);
3314
+ }
3315
+
3316
+ /**
3317
+ * Clean up branches in catch block after unexpected error.
3318
+ * Safe to delete both remote and local since no PR was created.
3319
+ */
3320
+ async function cleanupErrorBranches(branch, cwd, remoteBranchExists) {
3321
+ // Clean up remote branch if it exists (push may have succeeded before error).
3322
+ // Safe to delete both remote and local since no PR was created.
3323
+ if (remoteBranchExists) {
3324
+ await utils.gitDeleteRemoteBranch(branch, cwd);
3325
+ }
3326
+ await utils.gitDeleteBranch(branch, cwd);
3327
+ }
3328
+
3267
3329
  const GITHUB_ADVISORIES_URL = 'https://github.com/advisories';
3268
3330
  function getSocketFixBranchName(ghsaId) {
3269
3331
  return `socket/fix/${ghsaId}`;
@@ -3323,17 +3385,66 @@ async function openSocketFixPr(owner, repo, branch, ghsaIds, options) {
3323
3385
  require$$9.debugDir('inspect', {
3324
3386
  octokitPullsCreateParams
3325
3387
  });
3326
- return await octokit.pulls.create(octokitPullsCreateParams);
3388
+ const pr = await octokit.pulls.create(octokitPullsCreateParams);
3389
+ return {
3390
+ ok: true,
3391
+ pr
3392
+ };
3327
3393
  } catch (e) {
3328
- let message = `Failed to open pull request`;
3329
- const errors = e instanceof vendor.RequestError ? e.response?.data?.['errors'] : undefined;
3330
- if (Array.isArray(errors) && errors.length) {
3331
- const details = errors.map(d => `- ${d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`}`).join('\n');
3332
- message += `:\n${details}`;
3394
+ // Handle RequestError from Octokit.
3395
+ if (e instanceof vendor.RequestError) {
3396
+ const errors = e.response?.data?.['errors'];
3397
+ const errorMessages = Array.isArray(errors) ? errors.map(d => d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`) : [];
3398
+
3399
+ // Check for "PR already exists" error.
3400
+ if (errorMessages.some(msg => msg.toLowerCase().includes('pull request already exists'))) {
3401
+ require$$9.debugFn('error', 'Failed to open pull request: already exists');
3402
+ return {
3403
+ ok: false,
3404
+ reason: 'already_exists',
3405
+ error: e
3406
+ };
3407
+ }
3408
+
3409
+ // Check for validation errors (e.g., no commits between branches).
3410
+ if (errors && errors.length > 0) {
3411
+ const details = errorMessages.map(d => `- ${d}`).join('\n');
3412
+ require$$9.debugFn('error', `Failed to open pull request:\n${details}`);
3413
+ return {
3414
+ ok: false,
3415
+ reason: 'validation_error',
3416
+ error: e,
3417
+ details
3418
+ };
3419
+ }
3420
+
3421
+ // Check HTTP status codes.
3422
+ if (e.status === 403 || e.status === 401) {
3423
+ require$$9.debugFn('error', 'Failed to open pull request: permission denied');
3424
+ return {
3425
+ ok: false,
3426
+ reason: 'permission_denied',
3427
+ error: e
3428
+ };
3429
+ }
3430
+ if (e.status && e.status >= 500) {
3431
+ require$$9.debugFn('error', 'Failed to open pull request: network error');
3432
+ return {
3433
+ ok: false,
3434
+ reason: 'network_error',
3435
+ error: e
3436
+ };
3437
+ }
3333
3438
  }
3334
- require$$9.debugFn('error', message);
3439
+
3440
+ // Unknown error.
3441
+ require$$9.debugFn('error', `Failed to open pull request: ${e}`);
3442
+ return {
3443
+ ok: false,
3444
+ reason: 'unknown',
3445
+ error: e
3446
+ };
3335
3447
  }
3336
- return undefined;
3337
3448
  }
3338
3449
  async function getSocketFixPrs(owner, repo, options) {
3339
3450
  return (await getSocketFixPrsWithContext(owner, repo, options)).map(d => d.match);
@@ -3760,10 +3871,34 @@ async function coanaFix(fixConfig) {
3760
3871
  overallFixed = true;
3761
3872
  const branch = getSocketFixBranchName(ghsaId);
3762
3873
  try {
3763
- // Check if branch already exists.
3874
+ // Check if an open PR already exists for this GHSA.
3875
+ // eslint-disable-next-line no-await-in-loop
3876
+ const existingOpenPrs = await getSocketFixPrs(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, {
3877
+ ghsaId,
3878
+ states: constants.GQL_PR_STATE_OPEN
3879
+ });
3880
+ if (existingOpenPrs.length > 0) {
3881
+ const prNum = existingOpenPrs[0].number;
3882
+ logger.logger.info(`PR #${prNum} already exists for ${ghsaId}, skipping.`);
3883
+ require$$9.debugFn('notice', `skip: open PR #${prNum} exists for ${ghsaId}`);
3884
+ continue ghsaLoop;
3885
+ }
3886
+
3887
+ // If branch exists but no open PR, delete the stale branch.
3888
+ // This handles cases where PR creation failed but branch was pushed.
3764
3889
  // eslint-disable-next-line no-await-in-loop
3765
3890
  if (await utils.gitRemoteBranchExists(branch, cwd)) {
3766
- require$$9.debugFn('notice', `skip: remote branch "${branch}" exists`);
3891
+ // eslint-disable-next-line no-await-in-loop
3892
+ const shouldContinue = await cleanupStaleBranch(branch, ghsaId, cwd);
3893
+ if (!shouldContinue) {
3894
+ continue ghsaLoop;
3895
+ }
3896
+ }
3897
+
3898
+ // Check for GitHub token before doing any git operations.
3899
+ if (!fixEnv.githubToken) {
3900
+ logger.logger.error('Cannot create pull request: SOCKET_CLI_GITHUB_TOKEN environment variable is not set.\n' + 'Set SOCKET_CLI_GITHUB_TOKEN or GITHUB_TOKEN to enable PR creation.');
3901
+ require$$9.debugFn('error', `skip: missing GitHub token for ${ghsaId}`);
3767
3902
  continue ghsaLoop;
3768
3903
  }
3769
3904
  require$$9.debugFn('notice', `pr: creating for ${ghsaId}`);
@@ -3794,31 +3929,21 @@ async function coanaFix(fixConfig) {
3794
3929
  }
3795
3930
 
3796
3931
  // Set up git remote.
3797
- if (!fixEnv.githubToken) {
3798
- logger.logger.error('Cannot create pull request: SOCKET_CLI_GITHUB_TOKEN environment variable is not set.\n' + 'Set SOCKET_CLI_GITHUB_TOKEN or GITHUB_TOKEN to enable PR creation.');
3799
- // eslint-disable-next-line no-await-in-loop
3800
- await utils.gitResetAndClean(fixEnv.baseBranch, cwd);
3801
- // eslint-disable-next-line no-await-in-loop
3802
- await utils.gitCheckoutBranch(fixEnv.baseBranch, cwd);
3803
- // eslint-disable-next-line no-await-in-loop
3804
- await utils.gitDeleteBranch(branch, cwd);
3805
- continue ghsaLoop;
3806
- }
3807
3932
  // eslint-disable-next-line no-await-in-loop
3808
3933
  await utils.setGitRemoteGithubRepoUrl(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, fixEnv.githubToken, cwd);
3809
3934
 
3810
3935
  // eslint-disable-next-line no-await-in-loop
3811
- const prResponse = await openSocketFixPr(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, branch,
3936
+ const prResult = await openSocketFixPr(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, branch,
3812
3937
  // Single GHSA ID.
3813
3938
  [ghsaId], {
3814
3939
  baseBranch: fixEnv.baseBranch,
3815
3940
  cwd,
3816
3941
  ghsaDetails
3817
3942
  });
3818
- if (prResponse) {
3943
+ if (prResult.ok) {
3819
3944
  const {
3820
3945
  data
3821
- } = prResponse;
3946
+ } = prResult.pr;
3822
3947
  const prRef = `PR #${data.number}`;
3823
3948
  logger.logger.success(`Opened ${prRef} for ${ghsaId}.`);
3824
3949
  if (autopilot) {
@@ -3838,16 +3963,47 @@ async function coanaFix(fixConfig) {
3838
3963
  logger.logger.dedent();
3839
3964
  spinner?.dedent();
3840
3965
  }
3966
+
3967
+ // Clean up local branch only - keep remote branch for PR merge.
3968
+ // eslint-disable-next-line no-await-in-loop
3969
+ await cleanupSuccessfulPrLocalBranch(branch, cwd);
3970
+ } else {
3971
+ // Handle PR creation failures.
3972
+ if (prResult.reason === 'already_exists') {
3973
+ logger.logger.info(`PR already exists for ${ghsaId} (this should not happen due to earlier check).`);
3974
+ // Don't delete branch - PR exists and needs it.
3975
+ } else if (prResult.reason === 'validation_error') {
3976
+ logger.logger.error(`Failed to create PR for ${ghsaId}:\n${prResult.details}`);
3977
+ // eslint-disable-next-line no-await-in-loop
3978
+ await cleanupFailedPrBranches(branch, cwd);
3979
+ } else if (prResult.reason === 'permission_denied') {
3980
+ logger.logger.error(`Failed to create PR for ${ghsaId}: Permission denied. Check SOCKET_CLI_GITHUB_TOKEN permissions.`);
3981
+ // eslint-disable-next-line no-await-in-loop
3982
+ await cleanupFailedPrBranches(branch, cwd);
3983
+ } else if (prResult.reason === 'network_error') {
3984
+ logger.logger.error(`Failed to create PR for ${ghsaId}: Network error. Please try again.`);
3985
+ // eslint-disable-next-line no-await-in-loop
3986
+ await cleanupFailedPrBranches(branch, cwd);
3987
+ } else {
3988
+ logger.logger.error(`Failed to create PR for ${ghsaId}: ${prResult.error.message}`);
3989
+ // eslint-disable-next-line no-await-in-loop
3990
+ await cleanupFailedPrBranches(branch, cwd);
3991
+ }
3841
3992
  }
3842
3993
 
3843
3994
  // Reset back to base branch for next iteration.
3844
3995
  // eslint-disable-next-line no-await-in-loop
3845
- await utils.gitResetAndClean(branch, cwd);
3996
+ await utils.gitResetAndClean(fixEnv.baseBranch, cwd);
3846
3997
  // eslint-disable-next-line no-await-in-loop
3847
3998
  await utils.gitCheckoutBranch(fixEnv.baseBranch, cwd);
3848
3999
  } catch (e) {
3849
4000
  logger.logger.warn(`Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`);
3850
4001
  require$$9.debugDir('error', e);
4002
+ // Clean up branches (push may have succeeded before error).
4003
+ // eslint-disable-next-line no-await-in-loop
4004
+ const remoteBranchExists = await utils.gitRemoteBranchExists(branch, cwd);
4005
+ // eslint-disable-next-line no-await-in-loop
4006
+ await cleanupErrorBranches(branch, cwd, remoteBranchExists);
3851
4007
  // eslint-disable-next-line no-await-in-loop
3852
4008
  await utils.gitResetAndClean(fixEnv.baseBranch, cwd);
3853
4009
  // eslint-disable-next-line no-await-in-loop
@@ -15114,5 +15270,5 @@ void (async () => {
15114
15270
  await utils.captureException(e);
15115
15271
  }
15116
15272
  })();
15117
- //# debugId=af14f2c8-7f1a-4f12-bd1a-322165537e4f
15273
+ //# debugId=dbcc0fa8-7ea6-462d-9ebe-824e2129f7b8
15118
15274
  //# sourceMappingURL=cli.js.map