@socketsecurity/cli 1.1.29 → 1.1.31
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 +14 -0
- package/dist/cli.js +190 -27
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/fix/branch-cleanup.d.mts +23 -0
- package/dist/types/commands/fix/branch-cleanup.d.mts.map +1 -0
- package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
- package/dist/types/commands/fix/git.d.mts.map +1 -1
- package/dist/types/commands/fix/pull-request.d.mts +27 -1
- package/dist/types/commands/fix/pull-request.d.mts.map +1 -1
- package/dist/types/utils/dlx.d.mts.map +1 -1
- package/dist/utils.js +25 -4
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ 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.31](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.31) - 2025-11-19
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- Enhanced pull request descriptions to remove duplicate package listings for cleaner, more readable output
|
|
11
|
+
|
|
12
|
+
## [1.1.30](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.30) - 2025-11-18
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Enhanced `SOCKET_CLI_COANA_LOCAL_PATH` to support compiled Coana CLI binaries alongside Node.js script files
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Resolved PR creation workflow to properly recreate pull requests after closing or merging
|
|
19
|
+
- Corrected API token selection to honor `SOCKET_CLI_API_TOKEN` environment variable in package alert requests
|
|
20
|
+
|
|
7
21
|
## [1.1.29](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.29) - 2025-11-16
|
|
8
22
|
|
|
9
23
|
### Added
|
package/dist/cli.js
CHANGED
|
@@ -3264,7 +3264,76 @@ 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';
|
|
3330
|
+
|
|
3331
|
+
/**
|
|
3332
|
+
* Extract unique package names with ecosystems from vulnerability details.
|
|
3333
|
+
*/
|
|
3334
|
+
function getUniquePackages(details) {
|
|
3335
|
+
return [...new Set(details.vulnerabilities.nodes.map(v => `${v.package.name} (${v.package.ecosystem})`))];
|
|
3336
|
+
}
|
|
3268
3337
|
function getSocketFixBranchName(ghsaId) {
|
|
3269
3338
|
return `socket/fix/${ghsaId}`;
|
|
3270
3339
|
}
|
|
@@ -3284,14 +3353,14 @@ function getSocketFixPullRequestBody(ghsaIds, ghsaDetails) {
|
|
|
3284
3353
|
if (!details) {
|
|
3285
3354
|
return body;
|
|
3286
3355
|
}
|
|
3287
|
-
const packages = details
|
|
3356
|
+
const packages = getUniquePackages(details);
|
|
3288
3357
|
return [body, '', '', `**Vulnerability Summary:** ${details.summary}`, '', `**Severity:** ${details.severity}`, '', `**Affected Packages:** ${arrays.joinAnd(packages)}`].join('\n');
|
|
3289
3358
|
}
|
|
3290
3359
|
return [`[Socket](${constants.default.SOCKET_WEBSITE_URL}) fixes for ${vulnCount} GHSAs.`, '', '**Fixed Vulnerabilities:**', ...ghsaIds.map(id => {
|
|
3291
3360
|
const details = ghsaDetails?.get(id);
|
|
3292
3361
|
const item = `- [${id}](${GITHUB_ADVISORIES_URL}/${id})`;
|
|
3293
3362
|
if (details) {
|
|
3294
|
-
const packages = details
|
|
3363
|
+
const packages = getUniquePackages(details);
|
|
3295
3364
|
return `${item} - ${details.summary} (${arrays.joinAnd(packages)})`;
|
|
3296
3365
|
}
|
|
3297
3366
|
return item;
|
|
@@ -3323,17 +3392,66 @@ async function openSocketFixPr(owner, repo, branch, ghsaIds, options) {
|
|
|
3323
3392
|
require$$9.debugDir('inspect', {
|
|
3324
3393
|
octokitPullsCreateParams
|
|
3325
3394
|
});
|
|
3326
|
-
|
|
3395
|
+
const pr = await octokit.pulls.create(octokitPullsCreateParams);
|
|
3396
|
+
return {
|
|
3397
|
+
ok: true,
|
|
3398
|
+
pr
|
|
3399
|
+
};
|
|
3327
3400
|
} catch (e) {
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
const
|
|
3332
|
-
|
|
3401
|
+
// Handle RequestError from Octokit.
|
|
3402
|
+
if (e instanceof vendor.RequestError) {
|
|
3403
|
+
const errors = e.response?.data?.['errors'];
|
|
3404
|
+
const errorMessages = Array.isArray(errors) ? errors.map(d => d.message?.trim() ?? `${d.resource}.${d.field} (${d.code})`) : [];
|
|
3405
|
+
|
|
3406
|
+
// Check for "PR already exists" error.
|
|
3407
|
+
if (errorMessages.some(msg => msg.toLowerCase().includes('pull request already exists'))) {
|
|
3408
|
+
require$$9.debugFn('error', 'Failed to open pull request: already exists');
|
|
3409
|
+
return {
|
|
3410
|
+
ok: false,
|
|
3411
|
+
reason: 'already_exists',
|
|
3412
|
+
error: e
|
|
3413
|
+
};
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
// Check for validation errors (e.g., no commits between branches).
|
|
3417
|
+
if (errors && errors.length > 0) {
|
|
3418
|
+
const details = errorMessages.map(d => `- ${d}`).join('\n');
|
|
3419
|
+
require$$9.debugFn('error', `Failed to open pull request:\n${details}`);
|
|
3420
|
+
return {
|
|
3421
|
+
ok: false,
|
|
3422
|
+
reason: 'validation_error',
|
|
3423
|
+
error: e,
|
|
3424
|
+
details
|
|
3425
|
+
};
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
// Check HTTP status codes.
|
|
3429
|
+
if (e.status === 403 || e.status === 401) {
|
|
3430
|
+
require$$9.debugFn('error', 'Failed to open pull request: permission denied');
|
|
3431
|
+
return {
|
|
3432
|
+
ok: false,
|
|
3433
|
+
reason: 'permission_denied',
|
|
3434
|
+
error: e
|
|
3435
|
+
};
|
|
3436
|
+
}
|
|
3437
|
+
if (e.status && e.status >= 500) {
|
|
3438
|
+
require$$9.debugFn('error', 'Failed to open pull request: network error');
|
|
3439
|
+
return {
|
|
3440
|
+
ok: false,
|
|
3441
|
+
reason: 'network_error',
|
|
3442
|
+
error: e
|
|
3443
|
+
};
|
|
3444
|
+
}
|
|
3333
3445
|
}
|
|
3334
|
-
|
|
3446
|
+
|
|
3447
|
+
// Unknown error.
|
|
3448
|
+
require$$9.debugFn('error', `Failed to open pull request: ${e}`);
|
|
3449
|
+
return {
|
|
3450
|
+
ok: false,
|
|
3451
|
+
reason: 'unknown',
|
|
3452
|
+
error: e
|
|
3453
|
+
};
|
|
3335
3454
|
}
|
|
3336
|
-
return undefined;
|
|
3337
3455
|
}
|
|
3338
3456
|
async function getSocketFixPrs(owner, repo, options) {
|
|
3339
3457
|
return (await getSocketFixPrsWithContext(owner, repo, options)).map(d => d.match);
|
|
@@ -3760,10 +3878,34 @@ async function coanaFix(fixConfig) {
|
|
|
3760
3878
|
overallFixed = true;
|
|
3761
3879
|
const branch = getSocketFixBranchName(ghsaId);
|
|
3762
3880
|
try {
|
|
3763
|
-
// Check if
|
|
3881
|
+
// Check if an open PR already exists for this GHSA.
|
|
3882
|
+
// eslint-disable-next-line no-await-in-loop
|
|
3883
|
+
const existingOpenPrs = await getSocketFixPrs(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, {
|
|
3884
|
+
ghsaId,
|
|
3885
|
+
states: constants.GQL_PR_STATE_OPEN
|
|
3886
|
+
});
|
|
3887
|
+
if (existingOpenPrs.length > 0) {
|
|
3888
|
+
const prNum = existingOpenPrs[0].number;
|
|
3889
|
+
logger.logger.info(`PR #${prNum} already exists for ${ghsaId}, skipping.`);
|
|
3890
|
+
require$$9.debugFn('notice', `skip: open PR #${prNum} exists for ${ghsaId}`);
|
|
3891
|
+
continue ghsaLoop;
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
// If branch exists but no open PR, delete the stale branch.
|
|
3895
|
+
// This handles cases where PR creation failed but branch was pushed.
|
|
3764
3896
|
// eslint-disable-next-line no-await-in-loop
|
|
3765
3897
|
if (await utils.gitRemoteBranchExists(branch, cwd)) {
|
|
3766
|
-
|
|
3898
|
+
// eslint-disable-next-line no-await-in-loop
|
|
3899
|
+
const shouldContinue = await cleanupStaleBranch(branch, ghsaId, cwd);
|
|
3900
|
+
if (!shouldContinue) {
|
|
3901
|
+
continue ghsaLoop;
|
|
3902
|
+
}
|
|
3903
|
+
}
|
|
3904
|
+
|
|
3905
|
+
// Check for GitHub token before doing any git operations.
|
|
3906
|
+
if (!fixEnv.githubToken) {
|
|
3907
|
+
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.');
|
|
3908
|
+
require$$9.debugFn('error', `skip: missing GitHub token for ${ghsaId}`);
|
|
3767
3909
|
continue ghsaLoop;
|
|
3768
3910
|
}
|
|
3769
3911
|
require$$9.debugFn('notice', `pr: creating for ${ghsaId}`);
|
|
@@ -3794,31 +3936,21 @@ async function coanaFix(fixConfig) {
|
|
|
3794
3936
|
}
|
|
3795
3937
|
|
|
3796
3938
|
// 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
3939
|
// eslint-disable-next-line no-await-in-loop
|
|
3808
3940
|
await utils.setGitRemoteGithubRepoUrl(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, fixEnv.githubToken, cwd);
|
|
3809
3941
|
|
|
3810
3942
|
// eslint-disable-next-line no-await-in-loop
|
|
3811
|
-
const
|
|
3943
|
+
const prResult = await openSocketFixPr(fixEnv.repoInfo.owner, fixEnv.repoInfo.repo, branch,
|
|
3812
3944
|
// Single GHSA ID.
|
|
3813
3945
|
[ghsaId], {
|
|
3814
3946
|
baseBranch: fixEnv.baseBranch,
|
|
3815
3947
|
cwd,
|
|
3816
3948
|
ghsaDetails
|
|
3817
3949
|
});
|
|
3818
|
-
if (
|
|
3950
|
+
if (prResult.ok) {
|
|
3819
3951
|
const {
|
|
3820
3952
|
data
|
|
3821
|
-
} =
|
|
3953
|
+
} = prResult.pr;
|
|
3822
3954
|
const prRef = `PR #${data.number}`;
|
|
3823
3955
|
logger.logger.success(`Opened ${prRef} for ${ghsaId}.`);
|
|
3824
3956
|
if (autopilot) {
|
|
@@ -3838,16 +3970,47 @@ async function coanaFix(fixConfig) {
|
|
|
3838
3970
|
logger.logger.dedent();
|
|
3839
3971
|
spinner?.dedent();
|
|
3840
3972
|
}
|
|
3973
|
+
|
|
3974
|
+
// Clean up local branch only - keep remote branch for PR merge.
|
|
3975
|
+
// eslint-disable-next-line no-await-in-loop
|
|
3976
|
+
await cleanupSuccessfulPrLocalBranch(branch, cwd);
|
|
3977
|
+
} else {
|
|
3978
|
+
// Handle PR creation failures.
|
|
3979
|
+
if (prResult.reason === 'already_exists') {
|
|
3980
|
+
logger.logger.info(`PR already exists for ${ghsaId} (this should not happen due to earlier check).`);
|
|
3981
|
+
// Don't delete branch - PR exists and needs it.
|
|
3982
|
+
} else if (prResult.reason === 'validation_error') {
|
|
3983
|
+
logger.logger.error(`Failed to create PR for ${ghsaId}:\n${prResult.details}`);
|
|
3984
|
+
// eslint-disable-next-line no-await-in-loop
|
|
3985
|
+
await cleanupFailedPrBranches(branch, cwd);
|
|
3986
|
+
} else if (prResult.reason === 'permission_denied') {
|
|
3987
|
+
logger.logger.error(`Failed to create PR for ${ghsaId}: Permission denied. Check SOCKET_CLI_GITHUB_TOKEN permissions.`);
|
|
3988
|
+
// eslint-disable-next-line no-await-in-loop
|
|
3989
|
+
await cleanupFailedPrBranches(branch, cwd);
|
|
3990
|
+
} else if (prResult.reason === 'network_error') {
|
|
3991
|
+
logger.logger.error(`Failed to create PR for ${ghsaId}: Network error. Please try again.`);
|
|
3992
|
+
// eslint-disable-next-line no-await-in-loop
|
|
3993
|
+
await cleanupFailedPrBranches(branch, cwd);
|
|
3994
|
+
} else {
|
|
3995
|
+
logger.logger.error(`Failed to create PR for ${ghsaId}: ${prResult.error.message}`);
|
|
3996
|
+
// eslint-disable-next-line no-await-in-loop
|
|
3997
|
+
await cleanupFailedPrBranches(branch, cwd);
|
|
3998
|
+
}
|
|
3841
3999
|
}
|
|
3842
4000
|
|
|
3843
4001
|
// Reset back to base branch for next iteration.
|
|
3844
4002
|
// eslint-disable-next-line no-await-in-loop
|
|
3845
|
-
await utils.gitResetAndClean(
|
|
4003
|
+
await utils.gitResetAndClean(fixEnv.baseBranch, cwd);
|
|
3846
4004
|
// eslint-disable-next-line no-await-in-loop
|
|
3847
4005
|
await utils.gitCheckoutBranch(fixEnv.baseBranch, cwd);
|
|
3848
4006
|
} catch (e) {
|
|
3849
4007
|
logger.logger.warn(`Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`);
|
|
3850
4008
|
require$$9.debugDir('error', e);
|
|
4009
|
+
// Clean up branches (push may have succeeded before error).
|
|
4010
|
+
// eslint-disable-next-line no-await-in-loop
|
|
4011
|
+
const remoteBranchExists = await utils.gitRemoteBranchExists(branch, cwd);
|
|
4012
|
+
// eslint-disable-next-line no-await-in-loop
|
|
4013
|
+
await cleanupErrorBranches(branch, cwd, remoteBranchExists);
|
|
3851
4014
|
// eslint-disable-next-line no-await-in-loop
|
|
3852
4015
|
await utils.gitResetAndClean(fixEnv.baseBranch, cwd);
|
|
3853
4016
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -15114,5 +15277,5 @@ void (async () => {
|
|
|
15114
15277
|
await utils.captureException(e);
|
|
15115
15278
|
}
|
|
15116
15279
|
})();
|
|
15117
|
-
//# debugId=
|
|
15280
|
+
//# debugId=4f8753fd-5919-4ea2-a850-0b907ec7bd09
|
|
15118
15281
|
//# sourceMappingURL=cli.js.map
|