Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 89 additions & 45 deletions src/commands/fix/git.mts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { escapeRegExp } from '@socketsecurity/registry/lib/regexps'
import { spawn } from '@socketsecurity/registry/lib/spawn'

import constants from '../../constants.mts'
import { getPurlObject } from '../../utils/purl.mts'
import {
getPkgFullNameFromPurlObj,
getSocketDevPackageOverviewUrlFromPurl,
Expand All @@ -13,11 +14,6 @@ import {
import type { CResult } from '../../types.mts'
import type { SpawnOptions } from '@socketsecurity/registry/lib/spawn'

export type GetSocketPrTitlePatternOptions = {
purl?: string | undefined
workspace?: string | undefined
}

export type GitCreateAndPushBranchOptions = {
cwd?: string | undefined
email?: string | undefined
Expand All @@ -42,72 +38,120 @@ export function getBaseGitBranch(): string {
}

export function getSocketBranchName(
purl: string,
purl: string | PackageURL,
newVersion: string,
workspaceName?: string | undefined,
workspace?: string | undefined,
): string {
const purlObj = PackageURL.fromString(purl)
const maybeWorkspaceName = workspaceName
? `${formatBranchName(workspaceName)}-`
: ''
const maybeNamespace = purlObj.namespace
? `${formatBranchName(purlObj.namespace)}-`
const purlObj = getPurlObject(purl)
const fmtType = formatBranchName(purlObj.type)
const fmtWorkspace = workspace ? `${formatBranchName(workspace)}` : 'root'
const fmtMaybeNamespace = purlObj.namespace
? `${formatBranchName(purlObj.namespace)}--`
: ''
const fullName = `${maybeWorkspaceName}${maybeNamespace}${formatBranchName(purlObj.name)}`
return `socket/${fullName}-${formatBranchName(newVersion)}`
const fmtFullName = `${fmtMaybeNamespace}${formatBranchName(purlObj.name)}`
const fmtVersion = formatBranchName(purlObj.version!)
const fmtNewVersion = formatBranchName(newVersion)
return `socket/${fmtType}_${fmtWorkspace}_${fmtFullName}_${fmtVersion}_${fmtNewVersion}`
}

export function getSocketPrTitlePattern(
options?: GetSocketPrTitlePatternOptions | undefined,
export type SocketBranchPatternOptions = {
newVersion?: string | undefined
purl?: string | undefined
workspace?: string | undefined
}

export function getSocketBranchPattern(
options?: SocketBranchPatternOptions | undefined,
): RegExp {
const { purl, workspace } = {
const { newVersion, purl, workspace } = {
__proto__: null,
...options,
} as GetSocketPrTitlePatternOptions
const purlObj = purl ? PackageURL.fromString(purl) : null
const escapedPkgFullName = purlObj
? escapeRegExp(getPkgFullNameFromPurlObj(purlObj))
: '\\S+'
const escapedPkgVersion = purlObj ? escapeRegExp(purlObj.version!) : '\\S+'
const escapedWorkspaceDetails = workspace
? ` in ${escapeRegExp(workspace)}`
} as SocketBranchPatternOptions
const purlObj = purl ? getPurlObject(purl) : null
const escType = purlObj ? escapeRegExp(purlObj.type) : '[^_]+'
const escWorkspace = workspace
? `${escapeRegExp(formatBranchName(workspace))}`
: 'root'
const escMaybeNamespace = purlObj?.namespace
? `${escapeRegExp(formatBranchName(purlObj.namespace))}--`
: ''
const escFullName = purlObj
? `${escMaybeNamespace}${escapeRegExp(formatBranchName(purlObj.name))}`
: '[^_]+'
const escVersion = purlObj
? escapeRegExp(formatBranchName(purlObj.version!))
: '[^_]+'
const escNewVersion = newVersion
? escapeRegExp(formatBranchName(newVersion))
: '[^_]+'
return new RegExp(
`Bump ${escapedPkgFullName} from ${escapedPkgVersion} to \\S+${escapedWorkspaceDetails}`,
`^socket/(${escType})_(${escWorkspace})_(${escFullName})_(${escVersion})_(${escNewVersion})$`,
)
}

export type SocketBranchParser = (
branch: string,
) => SocketBranchParseResult | null

export type SocketBranchParseResult = {
newVersion: string
purl: PackageURL
workspace: string
}

export function createSocketBranchParser(
options?: SocketBranchPatternOptions | undefined,
): SocketBranchParser {
const pattern = getSocketBranchPattern(options)
return function parse(branch: string): SocketBranchParseResult | null {
const match = pattern.exec(branch)
if (!match) {
return null
}
const {
0: type,
1: workspace,
2: fullName,
3: version,
4: newVersion,
} = match
return {
newVersion,
purl: getPurlObject(`pkg:${type}/${fullName}@${version}`),
workspace,
} as SocketBranchParseResult
}
}

export function getSocketPullRequestTitle(
purl: string,
toVersion: string,
purl: string | PackageURL,
newVersion: string,
workspace?: string | undefined,
): string {
const purlObj = PackageURL.fromString(purl)
const pkgFullName = getPkgFullNameFromPurlObj(purlObj)
const workspaceDetails = workspace ? ` in ${workspace}` : ''
return `Bump ${pkgFullName} from ${purlObj.version} to ${toVersion}${workspaceDetails}`
const purlObj = getPurlObject(purl)
const fullName = getPkgFullNameFromPurlObj(purlObj)
return `Bump ${fullName} from ${purlObj.version} to ${newVersion}${workspace ? ` in ${workspace}` : ''}`
}

export function getSocketPullRequestBody(
purl: string,
purl: string | PackageURL,
newVersion: string,
workspaceName?: string | undefined,
workspace?: string | undefined,
): string {
const purlObj = PackageURL.fromString(purl)
const pkgFullName = getPkgFullNameFromPurlObj(purlObj)
const workspaceDetails = workspaceName ? ` in ${workspaceName}` : ''
return `Bump [${pkgFullName}](${getSocketDevPackageOverviewUrlFromPurl(purlObj)}) from ${purlObj.version} to ${newVersion}${workspaceDetails}.`
const purlObj = getPurlObject(purl)
const fullName = getPkgFullNameFromPurlObj(purlObj)
const pkgOverviewUrl = getSocketDevPackageOverviewUrlFromPurl(purlObj)
return `Bump [${fullName}](${pkgOverviewUrl}) from ${purlObj.version} to ${newVersion}${workspace ? ` in ${workspace}` : ''}.`
}

export function getSocketCommitMessage(
purl: string,
purl: string | PackageURL,
newVersion: string,
workspaceName?: string | undefined,
workspace?: string | undefined,
): string {
const purlObj = PackageURL.fromString(purl)
const pkgFullName = getPkgFullNameFromPurlObj(purlObj)
const workspaceDetails = workspaceName ? ` in ${workspaceName}` : ''
return `socket: Bump ${pkgFullName} from ${purlObj.version} to ${newVersion}${workspaceDetails}`
const purlObj = getPurlObject(purl)
const fullName = getPkgFullNameFromPurlObj(purlObj)
return `socket: Bump ${fullName} from ${purlObj.version} to ${newVersion}${workspace ? ` in ${workspace}` : ''}`
}

export async function gitCleanFdx(cwd = process.cwd()): Promise<void> {
Expand Down
46 changes: 37 additions & 9 deletions src/commands/fix/npm-fix.mts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { runScript } from '@socketsecurity/registry/lib/npm'
import {
fetchPackagePackument,
readPackageJson,
resolvePackageName,
} from '@socketsecurity/registry/lib/packages'
import { naturalCompare } from '@socketsecurity/registry/lib/sorts'

Expand All @@ -25,11 +26,11 @@ import {
import {
cleanupOpenPrs,
enablePrAutoMerge,
getGitHubEnvRepoInfo,
getGithubEnvRepoInfo,
getOpenSocketPrs,
openPr,
prExistForBranch,
setGitRemoteGitHubRepoUrl,
setGitRemoteGithubRepoUrl,
} from './open-pr.mts'
import { getAlertsMapOptions } from './shared.mts'
import constants from '../../constants.mts'
Expand Down Expand Up @@ -123,7 +124,7 @@ export async function npmFix(
githubToken
)

const repoInfo = isCi ? getGitHubEnvRepoInfo()! : null
const repoInfo = isCi ? getGithubEnvRepoInfo()! : null

spinner?.start()

Expand All @@ -135,6 +136,10 @@ export async function npmFix(
})
: []

if (openPrs.length) {
debugFn(`found: ${openPrs.length} open PRs`)
}

let count = isCi ? openPrs.length : 0

const arb = new Arborist({
Expand All @@ -148,8 +153,14 @@ export async function npmFix(
let alertsMap
try {
alertsMap = purls.length
? await getAlertsMapFromPurls(purls, getAlertsMapOptions({ limit }))
: await getAlertsMapFromArborist(arb, getAlertsMapOptions({ limit }))
? await getAlertsMapFromPurls(
purls,
getAlertsMapOptions({ limit: limit + openPrs.length }),
)
: await getAlertsMapFromArborist(
arb,
getAlertsMapOptions({ limit: limit + openPrs.length }),
)
} catch (e) {
spinner?.stop()
debugFn('catch: PURL API\n', e)
Expand All @@ -160,7 +171,9 @@ export async function npmFix(
}
}

const infoByPkgName = getCveInfoFromAlertsMap(alertsMap, { limit })
const infoByPkgName = getCveInfoFromAlertsMap(alertsMap, {
limit: limit + openPrs.length,
})
if (!infoByPkgName) {
spinner?.stop()
logger.info('No fixable vulns found.')
Expand Down Expand Up @@ -201,7 +214,21 @@ export async function npmFix(
i += 1
) {
const isLastInfoEntry = i === length - 1
const { 0: name, 1: infos } = sortedInfoEntries[i]!
const infoEntry = sortedInfoEntries[i]!
const { 0: name } = infoEntry
const openPrsForPkg = openPrs.filter(
pr => name === resolvePackageName(pr.purl),
)
const infos = [...infoEntry[1].values()].filter(
info =>
!openPrsForPkg.find(
pr => pr.newVersion === info.firstPatchedVersionIdentifier,
),
)

if (!infos.length) {
continue infoEntriesLoop
}

logger.log(`Processing vulns for ${name}:`)
logger.indent()
Expand Down Expand Up @@ -444,13 +471,14 @@ export async function npmFix(

// eslint-disable-next-line no-await-in-loop
await Promise.allSettled([
setGitRemoteGitHubRepoUrl(
setGitRemoteGithubRepoUrl(
repoInfo.owner,
repoInfo.repo,
githubToken,
cwd,
),
cleanupOpenPrs(repoInfo.owner, repoInfo.repo, newVersion, {
cleanupOpenPrs(repoInfo.owner, repoInfo.repo, {
newVersion,
purl: oldPurl,
workspace,
}),
Expand Down
Loading