mirror of
https://github.com/supabase/supabase.git
synced 2026-05-06 08:56:46 -04:00
chore: Bump vulnerable dependencies (#44428)
This PR bumps various dependencies to fix vulnerabilities. The logic for bumping packages has been taken out of `fix-audit-vulnerability` into a `bump-package` script. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Chores** * Removed unused development dependency from generator package * Updated package version overrides and vulnerability management configuration to address security concerns * Enhanced internal package dependency maintenance tooling for improved operational efficiency <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -22,7 +22,6 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@redocly/cli": "^1.28.0",
|
||||
"@types/json-stringify-safe": "^5.0.0",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
|
||||
Generated
+274
-1225
File diff suppressed because it is too large
Load Diff
+7
-9
@@ -46,12 +46,9 @@ minimumReleaseAgeExclude:
|
||||
- '@supabase/*'
|
||||
- '@supabase-labs/*'
|
||||
# The following deps were added due to vulnerabilities. You can remove them after the minimum time has passed.
|
||||
- undici
|
||||
- next
|
||||
- '@next/*'
|
||||
- h3
|
||||
- typescript
|
||||
- stripe-experiment-sync
|
||||
- path-to-regexp
|
||||
- brace-expansion
|
||||
- serialize-javascript
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- node-pty
|
||||
@@ -59,11 +56,12 @@ onlyBuiltDependencies:
|
||||
|
||||
overrides:
|
||||
'@ardatan/relay-compiler>immutable': ^3.8.3
|
||||
'@aws-sdk/core>fast-xml-parser': ^4.5.4
|
||||
'@aws-sdk/core>fast-xml-parser': ^4.5.5
|
||||
'@mapbox/node-pre-gyp>tar': ^7.5.11
|
||||
'@redocly/respect-core>form-data': ^4.0.4
|
||||
'@redocly/respect-core>js-yaml': ^4.1.1
|
||||
'@rollup/plugin-terser>serialize-javascript': ^7.0.3
|
||||
'@rollup/plugin-terser>serialize-javascript': ^7.0.5
|
||||
'@tanstack/start-server-core': ^1.159.0
|
||||
cacache>tar: ^7.5.11
|
||||
dompurify: ^3.3.2
|
||||
esbuild: ^0.25.2
|
||||
@@ -75,7 +73,7 @@ overrides:
|
||||
'pgsql-parser>libpg-query': ^15.2.0
|
||||
refractor>prismjs: ^1.30.0
|
||||
supabase>tar: ^7.5.11
|
||||
'terser-webpack-plugin>serialize-javascript': ^7.0.3
|
||||
'terser-webpack-plugin>serialize-javascript': ^7.0.5
|
||||
tmp: ^0.2.4
|
||||
webpack: ^5.104.1
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
import { execSync } from 'node:child_process'
|
||||
import * as fs from 'node:fs'
|
||||
import * as path from 'node:path'
|
||||
|
||||
const WORKSPACE_YAML_PATH = path.join(process.cwd(), 'pnpm-workspace.yaml')
|
||||
export const LOCKFILE_PATH = path.join(process.cwd(), 'pnpm-lock.yaml')
|
||||
|
||||
export function compareSemver(a: string, b: string): number {
|
||||
const pa = a.split('.').map(Number)
|
||||
const pb = b.split('.').map(Number)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (pa[i] !== pb[i]) return pa[i] - pb[i]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function escapeRegex(str: string): string {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
|
||||
function formatOverrideLine(moduleName: string, version: string): string {
|
||||
const key = moduleName.includes('@') ? `'${moduleName}'` : moduleName
|
||||
return ` ${key}: ${version}`
|
||||
}
|
||||
|
||||
function addOverride(yamlContent: string, moduleName: string, version: string): string {
|
||||
const lines = yamlContent.split('\n')
|
||||
|
||||
const overridesIdx = lines.findIndex((line) => /^overrides:\s*$/.test(line))
|
||||
if (overridesIdx === -1) {
|
||||
throw new Error('Could not find "overrides:" section in pnpm-workspace.yaml')
|
||||
}
|
||||
|
||||
let blockEnd = overridesIdx + 1
|
||||
while (blockEnd < lines.length) {
|
||||
const line = lines[blockEnd]
|
||||
if (line === '' || /^\s+/.test(line)) {
|
||||
blockEnd++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const existingPattern = new RegExp(`^\\s+['"]?${escapeRegex(moduleName)}['"]?\\s*:`)
|
||||
const existingIdx = lines.findIndex(
|
||||
(line, idx) => idx > overridesIdx && idx < blockEnd && existingPattern.test(line)
|
||||
)
|
||||
|
||||
if (existingIdx !== -1) {
|
||||
console.log(`\nWARNING: Override for "${moduleName}" already exists:`)
|
||||
console.log(` ${lines[existingIdx].trim()}`)
|
||||
console.log(` Replacing with: ${moduleName}: ${version}`)
|
||||
lines[existingIdx] = formatOverrideLine(moduleName, version)
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
const newLine = formatOverrideLine(moduleName, version)
|
||||
lines.splice(blockEnd, 0, newLine)
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
function findHighestVersionInLockfile(packageName: string): string | null {
|
||||
const lockfileContent = fs.readFileSync(LOCKFILE_PATH, 'utf-8')
|
||||
const escaped = escapeRegex(packageName)
|
||||
// pnpm lockfile v9 format:
|
||||
// scoped: ` '@org/pkg@x.y.z':`
|
||||
// unscoped: ` pkg@x.y.z:`
|
||||
const pattern = packageName.startsWith('@')
|
||||
? new RegExp(`^ '${escaped}@(\\d+\\.\\d+\\.\\d+)':`, 'gm')
|
||||
: new RegExp(`^ ${escaped}@(\\d+\\.\\d+\\.\\d+):`, 'gm')
|
||||
|
||||
const versions: string[] = []
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = pattern.exec(lockfileContent)) !== null) {
|
||||
versions.push(match[1])
|
||||
}
|
||||
|
||||
if (versions.length === 0) return null
|
||||
return versions.sort(compareSemver).at(-1)!
|
||||
}
|
||||
|
||||
function handleMinimumReleaseAgeError(output: string): void {
|
||||
const blocks = output.split('ERR_PNPM_NO_MATCHING_VERSION')
|
||||
for (const block of blocks.slice(1)) {
|
||||
const versionMatch = block.match(
|
||||
/No matching version found for (\S+) published by .+?\. Version (\S+) satisfies the specs but was released at (.+)/
|
||||
)
|
||||
const chainLines = block
|
||||
.split('\n')
|
||||
.filter((line: string) => /^\s+at /.test(line))
|
||||
.map((line: string) => line.trim().replace(/^at /, ''))
|
||||
|
||||
if (versionMatch) {
|
||||
const [, spec, version, releaseDate] = versionMatch
|
||||
console.error(`\n Blocked package: ${spec} (v${version} released ${releaseDate.trim()})`)
|
||||
if (chainLines.length > 0) {
|
||||
console.error(` Dependency chain: ${chainLines.join(' -> ')}`)
|
||||
}
|
||||
const pkgName = spec.replace(/@[^/]*$/, '')
|
||||
console.error(
|
||||
` To unblock, add "${pkgName}" to the minimumReleaseAgeExclude setting in pnpm-workspace.yaml`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface BumpResult {
|
||||
previousVersion: string | null
|
||||
finalVersion: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Bumps a package to the given override version (or patch+1 of the current highest if omitted).
|
||||
* First tries a plain `pnpm install`; if that doesn't move the version, falls back to adding
|
||||
* a temporary override in pnpm-workspace.yaml. Reverts and throws on failure.
|
||||
*/
|
||||
export async function bumpPackage(
|
||||
packageName: string,
|
||||
overrideVersion?: string
|
||||
): Promise<BumpResult> {
|
||||
const currentHighest = findHighestVersionInLockfile(packageName)
|
||||
|
||||
let targetVersion: string
|
||||
if (overrideVersion) {
|
||||
targetVersion = overrideVersion
|
||||
console.log(`Package: ${packageName}`)
|
||||
if (currentHighest) console.log(`Current highest in lockfile: ${currentHighest}`)
|
||||
console.log(`Target version (explicit): ${targetVersion}`)
|
||||
} else {
|
||||
if (!currentHighest) {
|
||||
throw new Error(`Package "${packageName}" not found in pnpm-lock.yaml`)
|
||||
}
|
||||
const [major, minor, patch] = currentHighest.split('.').map(Number)
|
||||
targetVersion = `^${major}.${minor}.${patch + 1}`
|
||||
console.log(`Package: ${packageName}`)
|
||||
console.log(`Current highest in lockfile: ${currentHighest}`)
|
||||
console.log(`Target version (minimal bump): ${targetVersion}`)
|
||||
}
|
||||
|
||||
// Attempt 1: plain pnpm install, no override
|
||||
console.log('\nAttempt 1: pnpm install (no override)...')
|
||||
execSync('pnpm install --silent', { stdio: 'pipe', encoding: 'utf-8' })
|
||||
|
||||
const afterPlainInstall = findHighestVersionInLockfile(packageName)
|
||||
if (currentHighest && afterPlainInstall && compareSemver(afterPlainInstall, currentHighest) > 0) {
|
||||
console.log(
|
||||
`\n${packageName} bumped to ${afterPlainInstall} via plain install (no override needed).`
|
||||
)
|
||||
return { previousVersion: currentHighest, finalVersion: afterPlainInstall }
|
||||
}
|
||||
|
||||
console.log('No change from plain install. Proceeding with override...')
|
||||
|
||||
// Snapshot for revert
|
||||
const originalYaml = fs.readFileSync(WORKSPACE_YAML_PATH, 'utf-8')
|
||||
const originalLockfile = fs.readFileSync(LOCKFILE_PATH, 'utf-8')
|
||||
|
||||
function revert(): void {
|
||||
console.log('\nReverting pnpm-workspace.yaml and pnpm-lock.yaml...')
|
||||
fs.writeFileSync(WORKSPACE_YAML_PATH, originalYaml, 'utf-8')
|
||||
fs.writeFileSync(LOCKFILE_PATH, originalLockfile, 'utf-8')
|
||||
console.log('Reverted to original state.')
|
||||
}
|
||||
|
||||
// Attempt 2: add override
|
||||
console.log(`\nAttempt 2: adding override ${packageName}: ${targetVersion}`)
|
||||
const updatedYaml = addOverride(originalYaml, packageName, targetVersion)
|
||||
fs.writeFileSync(WORKSPACE_YAML_PATH, updatedYaml, 'utf-8')
|
||||
console.log('Updated pnpm-workspace.yaml')
|
||||
|
||||
console.log('\nRunning pnpm install (with override)...')
|
||||
try {
|
||||
execSync('pnpm install', { stdio: 'pipe', encoding: 'utf-8' })
|
||||
} catch (error: any) {
|
||||
const output = (error.stdout ?? '') + (error.stderr ?? '')
|
||||
if (output.includes('ERR_PNPM_NO_MATCHING_VERSION')) {
|
||||
console.error(
|
||||
`\nNo matching version found for "${packageName}@${targetVersion}" — blocked by minimumReleaseAge.`
|
||||
)
|
||||
handleMinimumReleaseAgeError(output)
|
||||
revert()
|
||||
throw new Error(`Blocked by minimumReleaseAge`)
|
||||
}
|
||||
revert()
|
||||
throw error
|
||||
}
|
||||
|
||||
// Remove override and re-install to let the lockfile bump stick on its own
|
||||
console.log('\nRemoving override and running pnpm install again...')
|
||||
fs.writeFileSync(WORKSPACE_YAML_PATH, originalYaml, 'utf-8')
|
||||
execSync('pnpm install --silent', { stdio: 'pipe', encoding: 'utf-8' })
|
||||
|
||||
return {
|
||||
previousVersion: currentHighest,
|
||||
finalVersion: findHighestVersionInLockfile(packageName),
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const [packageName, versionArg] = process.argv.slice(2)
|
||||
|
||||
if (!packageName) {
|
||||
console.error('Usage: ts-node scripts/bump-package.ts <package-name> [version]')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const overrideVersion = versionArg ? `^${versionArg}` : undefined
|
||||
|
||||
let result: BumpResult
|
||||
try {
|
||||
result = await bumpPackage(packageName, overrideVersion)
|
||||
} catch (error: any) {
|
||||
console.error(error.message ?? error)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const { previousVersion, finalVersion } = result
|
||||
if (!finalVersion || (previousVersion && compareSemver(finalVersion, previousVersion) <= 0)) {
|
||||
console.error(
|
||||
`\nERROR: "${packageName}" was not bumped after removing override. ` +
|
||||
`Consider using scoped overrides or updating the parent dependency.`
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(`\nSUCCESS: ${packageName} bumped from ${previousVersion} to ${finalVersion}.`)
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch((error) => {
|
||||
console.error('Fatal error:', error)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { execSync } from 'node:child_process'
|
||||
import * as fs from 'node:fs'
|
||||
import * as path from 'node:path'
|
||||
import * as readline from 'node:readline'
|
||||
import { bumpPackage, compareSemver, LOCKFILE_PATH } from './bump-package'
|
||||
|
||||
interface Advisory {
|
||||
id: number
|
||||
@@ -33,9 +33,6 @@ interface VulnerableModule {
|
||||
allPaths: string[]
|
||||
}
|
||||
|
||||
const WORKSPACE_YAML_PATH = path.join(process.cwd(), 'pnpm-workspace.yaml')
|
||||
const LOCKFILE_PATH = path.join(process.cwd(), 'pnpm-lock.yaml')
|
||||
|
||||
const SEVERITY_ORDER = ['critical', 'high', 'moderate', 'low']
|
||||
|
||||
function runAudit(): AuditOutput {
|
||||
@@ -60,15 +57,6 @@ function parseMinVersion(patchedVersions: string): string | null {
|
||||
return match ? match[1] : null
|
||||
}
|
||||
|
||||
function compareSemver(a: string, b: string): number {
|
||||
const pa = a.split('.').map(Number)
|
||||
const pb = b.split('.').map(Number)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (pa[i] !== pb[i]) return pa[i] - pb[i]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function groupAdvisories(advisories: Record<string, Advisory>): VulnerableModule[] {
|
||||
const byModule = new Map<string, Advisory[]>()
|
||||
|
||||
@@ -167,54 +155,6 @@ function promptSelection(modules: VulnerableModule[]): Promise<VulnerableModule>
|
||||
})
|
||||
}
|
||||
|
||||
function escapeRegex(str: string): string {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||
}
|
||||
|
||||
function formatOverrideLine(moduleName: string, version: string): string {
|
||||
const key = moduleName.includes('@') ? `'${moduleName}'` : moduleName
|
||||
return ` ${key}: ${version}`
|
||||
}
|
||||
|
||||
function addOverride(yamlContent: string, moduleName: string, version: string): string {
|
||||
const lines = yamlContent.split('\n')
|
||||
|
||||
const overridesIdx = lines.findIndex((line) => /^overrides:\s*$/.test(line))
|
||||
if (overridesIdx === -1) {
|
||||
throw new Error('Could not find "overrides:" section in pnpm-workspace.yaml')
|
||||
}
|
||||
|
||||
// Find the end of the overrides block (next non-indented, non-empty line)
|
||||
let blockEnd = overridesIdx + 1
|
||||
while (blockEnd < lines.length) {
|
||||
const line = lines[blockEnd]
|
||||
if (line === '' || /^\s+/.test(line)) {
|
||||
blockEnd++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this module already has an override
|
||||
const existingPattern = new RegExp(`^\\s+['"]?${escapeRegex(moduleName)}['"]?\\s*:`)
|
||||
const existingIdx = lines.findIndex(
|
||||
(line, idx) => idx > overridesIdx && idx < blockEnd && existingPattern.test(line)
|
||||
)
|
||||
|
||||
if (existingIdx !== -1) {
|
||||
console.log(`\nWARNING: Override for "${moduleName}" already exists:`)
|
||||
console.log(` ${lines[existingIdx].trim()}`)
|
||||
console.log(` Replacing with: ${moduleName}: ${version}`)
|
||||
lines[existingIdx] = formatOverrideLine(moduleName, version)
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
// Insert new override at the end of the overrides block
|
||||
const newLine = formatOverrideLine(moduleName, version)
|
||||
lines.splice(blockEnd, 0, newLine)
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
console.log('Running pnpm audit...')
|
||||
const auditResult = runAudit()
|
||||
@@ -230,74 +170,16 @@ async function main(): Promise<void> {
|
||||
|
||||
const selected = await promptSelection(modules)
|
||||
|
||||
// Snapshot original files for revert on failure
|
||||
const originalYaml = fs.readFileSync(WORKSPACE_YAML_PATH, 'utf-8')
|
||||
// Snapshot lockfile to revert if the audit verify step fails
|
||||
const originalLockfile = fs.readFileSync(LOCKFILE_PATH, 'utf-8')
|
||||
|
||||
function revert(): void {
|
||||
console.log('\nReverting pnpm-workspace.yaml and pnpm-lock.yaml...')
|
||||
fs.writeFileSync(WORKSPACE_YAML_PATH, originalYaml, 'utf-8')
|
||||
fs.writeFileSync(LOCKFILE_PATH, originalLockfile, 'utf-8')
|
||||
console.log('Reverted to original state.')
|
||||
}
|
||||
|
||||
console.log(`\nAdding override: ${selected.module_name}: ${selected.overrideVersion}`)
|
||||
const updatedYaml = addOverride(originalYaml, selected.module_name, selected.overrideVersion)
|
||||
fs.writeFileSync(WORKSPACE_YAML_PATH, updatedYaml, 'utf-8')
|
||||
console.log('Updated pnpm-workspace.yaml')
|
||||
|
||||
console.log('\nRunning pnpm install (with override)...')
|
||||
try {
|
||||
execSync('pnpm install', {
|
||||
stdio: 'pipe',
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
await bumpPackage(selected.module_name, selected.overrideVersion)
|
||||
} catch (error: any) {
|
||||
const output = (error.stdout ?? '') + (error.stderr ?? '')
|
||||
if (output.includes('ERR_PNPM_NO_MATCHING_VERSION')) {
|
||||
console.error(
|
||||
`\nNo matching version found for "${selected.module_name}@${selected.overrideVersion}", the minimumReleaseAge option forbids it from installing.`
|
||||
)
|
||||
|
||||
// Extract and display dependency chains that failed due to minimumReleaseAge
|
||||
const blocks = output.split('ERR_PNPM_NO_MATCHING_VERSION')
|
||||
for (const block of blocks.slice(1)) {
|
||||
const versionMatch = block.match(
|
||||
/No matching version found for (\S+) published by .+?\. Version (\S+) satisfies the specs but was released at (.+)/
|
||||
)
|
||||
const chainLines = block
|
||||
.split('\n')
|
||||
.filter((line: string) => /^\s+at /.test(line))
|
||||
.map((line: string) => line.trim().replace(/^at /, ''))
|
||||
|
||||
if (versionMatch) {
|
||||
const [, spec, version, releaseDate] = versionMatch
|
||||
console.error(`\n Blocked package: ${spec} (v${version} released ${releaseDate.trim()})`)
|
||||
if (chainLines.length > 0) {
|
||||
console.error(` Dependency chain: ${chainLines.join(' -> ')}`)
|
||||
}
|
||||
// Handle scoped (@org/pkg) and unscoped packages: strip the version range suffix
|
||||
const pkgName = spec.replace(/@[^/]*$/, '')
|
||||
console.error(
|
||||
` To unblock, add "${pkgName}" to the minimumReleaseAgeExclude setting in pnpm-workspace.yaml`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
revert()
|
||||
process.exit(1)
|
||||
}
|
||||
throw error
|
||||
console.error(error.message ?? error)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Remove the override and re-install to see if the lockfile update alone fixes it
|
||||
console.log('\nRemoving override and running pnpm install again...')
|
||||
fs.writeFileSync(WORKSPACE_YAML_PATH, originalYaml, 'utf-8')
|
||||
execSync('pnpm install --silent', {
|
||||
stdio: 'pipe',
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
|
||||
console.log('\nRunning pnpm audit to verify fix without override...')
|
||||
const verifyResult = runAudit()
|
||||
|
||||
@@ -306,7 +188,9 @@ async function main(): Promise<void> {
|
||||
)
|
||||
|
||||
if (stillVulnerable) {
|
||||
revert()
|
||||
console.log('\nReverting pnpm-lock.yaml...')
|
||||
fs.writeFileSync(LOCKFILE_PATH, originalLockfile, 'utf-8')
|
||||
console.log('Reverted to original state.')
|
||||
console.error(
|
||||
`\nERROR: Vulnerability for "${selected.module_name}" still present even with override.`
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user