@@ -10,6 +10,7 @@ import { Octokit } from '@octokit/rest'
1010import semver from 'semver'
1111
1212import { PackageURL } from '@socketregistry/packageurl-js'
13+ import { joinAnd } from '@socketsecurity/registry/lib/arrays'
1314import { debugDir , debugFn , isDebug } from '@socketsecurity/registry/lib/debug'
1415import {
1516 readJson ,
@@ -139,6 +140,96 @@ export async function cacheFetch<T>(
139140 return data
140141}
141142
143+ export type GhsaDetails = {
144+ ghsaId : string
145+ cveId ?: string
146+ summary : string
147+ severity : string
148+ publishedAt : string
149+ withdrawnAt ?: string
150+ references : Array < {
151+ url : string
152+ } >
153+ vulnerabilities : {
154+ nodes : Array < {
155+ package : {
156+ ecosystem : string
157+ name : string
158+ }
159+ vulnerableVersionRange : string
160+ } >
161+ }
162+ }
163+
164+ export async function fetchGhsaDetails (
165+ ids : string [ ] ,
166+ ) : Promise < Map < string , GhsaDetails > > {
167+ const results = new Map < string , GhsaDetails > ( )
168+ if ( ! ids . length ) {
169+ return results
170+ }
171+ const octokitGraphql = getOctokitGraphql ( )
172+ try {
173+ const gqlCacheKey = `${ ids . join ( '-' ) } -graphql-snapshot`
174+ const gqlResp = await cacheFetch ( gqlCacheKey , ( ) =>
175+ octokitGraphql (
176+ `
177+ query($identifiers: [SecurityAdvisoryIdentifierFilter!]!) {
178+ securityAdvisories(first: ${ ids . length } , identifiers: $identifiers) {
179+ nodes {
180+ ghsaId
181+ cveId
182+ summary
183+ severity
184+ publishedAt
185+ withdrawnAt
186+ references {
187+ url
188+ }
189+ vulnerabilities(first: 10) {
190+ nodes {
191+ package {
192+ ecosystem
193+ name
194+ }
195+ vulnerableVersionRange
196+ }
197+ }
198+ }
199+ }
200+ }` ,
201+ {
202+ identifiers : ids . map ( id => ( {
203+ type : 'GHSA' ,
204+ value : id ,
205+ } ) ) ,
206+ } ,
207+ ) ,
208+ )
209+
210+ const advisories : GhsaDetails [ ] =
211+ ( gqlResp as any ) ?. securityAdvisories ?. nodes || [ ]
212+ for ( const advisory of advisories ) {
213+ if ( advisory . ghsaId ) {
214+ results . set ( advisory . ghsaId , advisory )
215+ }
216+ }
217+
218+ // Log any missing advisories
219+ for ( const id of ids ) {
220+ if ( ! results . has ( id ) ) {
221+ debugFn ( 'notice' , `No advisory found for ${ id } ` )
222+ }
223+ }
224+ } catch ( e ) {
225+ debugFn (
226+ 'error' ,
227+ `Failed to fetch GHSA details: ${ ( e as Error ) ?. message || 'Unknown error' } ` ,
228+ )
229+ }
230+ return results
231+ }
232+
142233export type CleanupPrsOptions = {
143234 newVersion ?: string | undefined
144235 purl ?: string | undefined
@@ -243,9 +334,8 @@ export async function enablePrAutoMerge({
243334 node_id : prId ,
244335} : Pr ) : Promise < PrAutoMergeState > {
245336 const octokitGraphql = getOctokitGraphql ( )
246- let error : unknown
247337 try {
248- const response = await octokitGraphql (
338+ const gqlResp = await octokitGraphql (
249339 `
250340 mutation EnableAutoMerge($pullRequestId: ID!) {
251341 enablePullRequestAutoMerge(input: {
@@ -259,21 +349,20 @@ export async function enablePrAutoMerge({
259349 }` ,
260350 { pullRequestId : prId } ,
261351 )
262- const respPrNumber = ( response as any ) ?. enablePullRequestAutoMerge
352+ const respPrNumber = ( gqlResp as any ) ?. enablePullRequestAutoMerge
263353 ?. pullRequest ?. number
264354 if ( respPrNumber ) {
265355 return { enabled : true }
266356 }
267357 } catch ( e ) {
268- error = e
269- }
270- if (
271- error instanceof GraphqlResponseError &&
272- Array . isArray ( error . errors ) &&
273- error . errors . length
274- ) {
275- const details = error . errors . map ( ( { message : m } ) => m . trim ( ) )
276- return { enabled : false , details }
358+ if (
359+ e instanceof GraphqlResponseError &&
360+ Array . isArray ( e . errors ) &&
361+ e . errors . length
362+ ) {
363+ const details = e . errors . map ( ( { message : m } ) => m . trim ( ) )
364+ return { enabled : false , details }
365+ }
277366 }
278367 return { enabled : false }
279368}
@@ -371,6 +460,7 @@ async function getSocketPrsWithContext(
371460 state : GQL_PR_STATE
372461 title : string
373462 }
463+
374464 const nodes : GqlPrNode [ ] =
375465 ( gqlResp as any ) ?. repository ?. pullRequests ?. nodes ?? [ ]
376466 for ( let i = 0 , { length } = nodes ; i < length ; i += 1 ) {
@@ -516,6 +606,7 @@ export async function openPr(
516606export type OpenCoanaPrOptions = {
517607 baseBranch ?: string | undefined
518608 cwd ?: string | undefined
609+ ghsaDetails ?: Map < string , GhsaDetails > | undefined
519610}
520611
521612export async function openCoanaPr (
@@ -525,7 +616,7 @@ export async function openCoanaPr(
525616 ghsaIds : string [ ] ,
526617 options ?: OpenCoanaPrOptions | undefined ,
527618) : Promise < OctokitResponse < Pr > | null > {
528- const { baseBranch = 'main' } = {
619+ const { baseBranch = 'main' , ghsaDetails } = {
529620 __proto__ : null ,
530621 ...options ,
531622 } as OpenCoanaPrOptions
@@ -538,9 +629,42 @@ export async function openCoanaPr(
538629
539630 let prBody = ''
540631 if ( vulnCount === 1 ) {
541- prBody = `[Socket](https://socket.dev/) fix for [${ ghsaIds [ 0 ] } ](https://github.com/advisories/${ ghsaIds [ 0 ] } ).`
632+ const ghsaId = ghsaIds [ 0 ] !
633+ const details = ghsaDetails ?. get ( ghsaId )
634+
635+ prBody = `[Socket](https://socket.dev/) fix for [${ ghsaId } ](https://github.com/advisories/${ ghsaId } ).`
636+ if ( details ) {
637+ const packages = details . vulnerabilities . nodes . map (
638+ v => `${ v . package . name } (${ v . package . ecosystem } )` ,
639+ )
640+
641+ prBody += [
642+ '' ,
643+ '' ,
644+ `**Vulnerability Summary:** ${ details . summary } ` ,
645+ '' ,
646+ `**Severity:** ${ details . severity } ` ,
647+ '' ,
648+ `**Affected Packages:** ${ joinAnd ( packages ) } ` ,
649+ ] . join ( '\n' )
650+ }
542651 } else {
543- prBody = `[Socket](https://socket.dev/) fixes for ${ vulnCount } GHSAs.\n\n**Fixed GHSAs:**\n${ ghsaIds . map ( id => `- [${ id } ](https://github.com/advisories/${ id } )` ) . join ( '\n' ) } `
652+ prBody = [
653+ `[Socket](https://socket.dev/) fixes for ${ vulnCount } GHSAs.` ,
654+ '' ,
655+ '**Fixed Vulnerabilities:**' ,
656+ ...ghsaIds . map ( id => {
657+ const details = ghsaDetails ?. get ( id )
658+ const item = `- [${ id } ](https://github.com/advisories/${ id } )`
659+ if ( details ) {
660+ const packages = details . vulnerabilities . nodes . map (
661+ v => `${ v . package . name } ` ,
662+ )
663+ return `${ item } - ${ details . summary } (${ joinAnd ( packages ) } )`
664+ }
665+ return item
666+ } ) ,
667+ ] . join ( '\n' )
544668 }
545669
546670 try {
0 commit comments