11'use client'
22
3+ import { useState } from 'react'
34import {
45 Dialog ,
56 DialogContent ,
@@ -10,8 +11,18 @@ import {
1011 CardContent ,
1112 CardHeader ,
1213 CardTitle ,
14+ Button ,
1315} from '@/components/ui'
14- import { AlertTriangle , Info , Calendar , Shield , ExternalLink , Bug } from 'lucide-react'
16+ import {
17+ AlertTriangle ,
18+ Info ,
19+ Calendar ,
20+ Shield ,
21+ ExternalLink ,
22+ Bug ,
23+ ChevronDown ,
24+ ChevronUp ,
25+ } from 'lucide-react'
1526
1627interface CVSSInfo {
1728 vuln_impact : number
@@ -85,6 +96,17 @@ export default function TIXDetailsModal({
8596 repositoryName,
8697 translations,
8798} : TIXDetailsModalProps ) {
99+ const [ expandedPayloads , setExpandedPayloads ] = useState < Set < string > > ( new Set ( ) )
100+
101+ const togglePayload = ( exploitId : string ) => {
102+ const newExpanded = new Set ( expandedPayloads )
103+ if ( newExpanded . has ( exploitId ) ) {
104+ newExpanded . delete ( exploitId )
105+ } else {
106+ newExpanded . add ( exploitId )
107+ }
108+ setExpandedPayloads ( newExpanded )
109+ }
88110 const formatDate = ( dateString : string ) => {
89111 if ( ! dateString ) return 'N/A'
90112 return new Date ( dateString ) . toLocaleDateString ( 'es-ES' , {
@@ -478,16 +500,99 @@ export default function TIXDetailsModal({
478500 { translations . tixDetailsModal ?. knownExploits ||
479501 'Known Exploits' }
480502 </ p >
481- < Badge
482- variant = {
483- statement . exploits . length > 0 ? 'destructive' : 'secondary'
484- }
485- className = "text-xs"
486- >
487- { statement . exploits . length > 0
488- ? `${ statement . exploits . length } ${ translations . tixDetailsModal ?. available || 'available' } `
489- : translations . tixDetailsModal ?. notKnown || 'Not known' }
490- </ Badge >
503+ { statement . exploits . length > 0 ? (
504+ < div className = "space-y-2" >
505+ < Badge variant = "destructive" className = "text-xs mb-2" >
506+ { statement . exploits . length } { ' ' }
507+ { translations . tixDetailsModal ?. available || 'available' }
508+ </ Badge >
509+ < div className = "space-y-2 max-h-32 sm:max-h-40 overflow-y-auto" >
510+ { statement . exploits . map (
511+ ( exploit : any , exploitIndex : number ) => {
512+ const exploitId = `${ index } -${ exploitIndex } `
513+ const isExpanded = expandedPayloads . has ( exploitId )
514+ const shouldTruncate =
515+ exploit . payload && exploit . payload . length > 500
516+
517+ return (
518+ < div
519+ key = { exploitIndex }
520+ className = "bg-muted/30 rounded-lg p-2 sm:p-3"
521+ >
522+ < div className = "flex flex-col sm:flex-row sm:items-center gap-2 mb-2" >
523+ < a
524+ href = { exploit [ '@id' ] }
525+ target = "_blank"
526+ rel = "noopener noreferrer"
527+ className = "text-sm font-medium text-blue-600 hover:text-blue-800 hover:underline flex items-center gap-1 break-all"
528+ >
529+ { exploit . name }
530+ < ExternalLink className = "h-3 w-3 flex-shrink-0" />
531+ </ a >
532+ { exploit . attack_vector &&
533+ exploit . attack_vector !== 'NONE' && (
534+ < Badge variant = "outline" className = "text-xs" >
535+ { exploit . attack_vector }
536+ </ Badge >
537+ ) }
538+ </ div >
539+ { exploit . description &&
540+ exploit . description . trim ( ) && (
541+ < p className = "text-xs text-muted-foreground line-clamp-2 mb-2" >
542+ { exploit . description }
543+ </ p >
544+ ) }
545+ { exploit . payload && (
546+ < div className = "bg-background border rounded p-2" >
547+ < div className = "flex items-center justify-between mb-1" >
548+ < p className = "text-xs font-medium text-muted-foreground" >
549+ Payload/Details:
550+ </ p >
551+ { shouldTruncate && (
552+ < Button
553+ variant = "ghost"
554+ size = "sm"
555+ onClick = { ( ) => togglePayload ( exploitId ) }
556+ className = "h-6 px-2 text-xs"
557+ >
558+ { isExpanded ? (
559+ < >
560+ < ChevronUp className = "h-3 w-3 mr-1" />
561+ Show less
562+ </ >
563+ ) : (
564+ < >
565+ < ChevronDown className = "h-3 w-3 mr-1" />
566+ Show more
567+ </ >
568+ ) }
569+ </ Button >
570+ ) }
571+ </ div >
572+ < div
573+ className = { `text-xs font-mono text-gray-700 dark:text-gray-300 overflow-y-auto ${
574+ isExpanded ? 'max-h-96' : 'max-h-20'
575+ } `}
576+ >
577+ < pre className = "whitespace-pre-wrap break-words" >
578+ { isExpanded || ! shouldTruncate
579+ ? exploit . payload
580+ : `${ exploit . payload . substring ( 0 , 500 ) } ...` }
581+ </ pre >
582+ </ div >
583+ </ div >
584+ ) }
585+ </ div >
586+ )
587+ }
588+ ) }
589+ </ div >
590+ </ div >
591+ ) : (
592+ < Badge variant = "secondary" className = "text-xs" >
593+ { translations . tixDetailsModal ?. notKnown || 'Not known' }
594+ </ Badge >
595+ ) }
491596 </ div >
492597 </ div >
493598
0 commit comments