11import path from 'node:path' ;
2- import { promises as fs } from 'node:fs' ;
2+ import { promises as fs , existsSync , readFileSync } from 'node:fs' ;
33
44import semver from 'semver' ;
55import { replaceInFile } from 'replace-in-file' ;
@@ -21,7 +21,11 @@ const isWindows = process.platform === 'win32';
2121export default class ReleasePreparation extends Session {
2222 constructor ( argv , cli , dir ) {
2323 super ( cli , dir ) ;
24- this . isSecurityRelease = argv . security ;
24+ // argv.security can be either:
25+ // - true (boolean) if --security was used without parameter
26+ // - string if --security=path was used
27+ this . isSecurityRelease = ! ! argv . security ;
28+ this . securityReleaseRepo = typeof argv . security === 'string' ? argv . security : null ;
2529 this . isLTS = false ;
2630 this . isLTSTransition = argv . startLTS ;
2731 this . runBranchDiff = ! argv . skipBranchDiff ;
@@ -63,17 +67,61 @@ export default class ReleasePreparation extends Session {
6367 return false ;
6468 }
6569
70+ const vulnCveMap = new Map ( ) ;
71+ if ( this . isSecurityRelease && this . securityReleaseRepo ) {
72+ const vulnPath = path . join (
73+ this . securityReleaseRepo ,
74+ 'security-release' ,
75+ 'next-security-release' ,
76+ 'vulnerabilities.json'
77+ ) ;
78+
79+ if ( ! existsSync ( vulnPath ) ) {
80+ cli . error ( `vulnerabilities.json not found at ${ vulnPath } . ` +
81+ 'Skipping CVE auto-population.' ) ;
82+ cli . warn ( 'PRs will require manual CVE-ID entry.' ) ;
83+ } else {
84+ try {
85+ cli . startSpinner ( `Reading vulnerabilities.json from ${ vulnPath } ..` ) ;
86+ const vulnData = JSON . parse ( readFileSync ( vulnPath , 'utf-8' ) ) ;
87+ cli . stopSpinner ( `Done reading vulnerabilities.json from ${ vulnPath } ` ) ;
88+
89+ if ( vulnData . reports && Array . isArray ( vulnData . reports ) ) {
90+ vulnData . reports . forEach ( report => {
91+ if ( report . prURL && report . cveIds && report . cveIds . length > 0 ) {
92+ vulnCveMap . set ( report . prURL , report . cveIds ) ;
93+ }
94+ } ) ;
95+ }
96+ cli . ok ( `Loaded ${ vulnCveMap . size } CVE mappings from vulnerabilities.json` ) ;
97+ } catch ( err ) {
98+ cli . error ( `Failed to read vulnerabilities.json: ${ err . message } ` ) ;
99+ cli . warn ( 'Continuing without CVE auto-population.' ) ;
100+ }
101+ }
102+ }
103+
66104 for ( const pr of prs ) {
67105 if ( pr . mergeable !== 'MERGEABLE' ) {
68106 this . warnForNonMergeablePR ( pr ) ;
69107 }
108+
109+ // Look up CVE-IDs from vulnerabilities.json
110+ const prUrl = `https://github.com/${ this . owner } /${ this . repo } /pull/${ pr . number } ` ;
111+ const cveIds = vulnCveMap . get ( prUrl ) ;
112+
113+ if ( ! cveIds || cveIds . length === 0 ) {
114+ cli . warn ( `No CVE-IDs found in vulnerabilities.json for ${ prUrl } ` ) ;
115+ }
116+
70117 const cp = new CherryPick ( pr . number , this . dir , cli , {
71118 owner : this . owner ,
72119 repo : this . repo ,
73120 gpgSign : this . gpgSign ,
74121 upstream : this . isSecurityRelease ? `https://${ this . username } :${ this . config . token } @github.com/${ this . owner } /${ this . repo } .git` : this . upstream ,
75122 lint : false ,
76- includeCVE : true
123+ includeCVE : true ,
124+ cveIds : cveIds || null
77125 } ) ;
78126 const success = await cp . start ( ) ;
79127 if ( ! success ) {
0 commit comments