@@ -59,12 +59,71 @@ const ScanResults = ({
5959
6060 const { summary, findings, recommendedFixes, rateLimit } = results ;
6161
62- const severityOrder = [ 'CRITICAL' , 'HIGH' , 'MEDIUM' , 'LOW' ] ;
63- const severityIcons = {
64- CRITICAL : AlertTriangle ,
65- HIGH : AlertCircle ,
66- MEDIUM : AlertCircle ,
67- LOW : Info
62+ // Add safety check for findings
63+ if ( ! findings || typeof findings !== 'object' ) {
64+ console . error ( 'Invalid findings structure' ) ;
65+ return (
66+ < Alert variant = "error" >
67+ < AlertDescription >
68+ Invalid scan results structure. Please try again.
69+ </ AlertDescription >
70+ </ Alert >
71+ ) ;
72+ }
73+
74+ // Add safety check for recommendedFixes
75+ const safeRecommendedFixes = recommendedFixes && Array . isArray ( recommendedFixes )
76+ ? recommendedFixes
77+ : [ ] ;
78+
79+ // Helper function to get severity icon
80+ const getSeverityIcon = ( severity ) => {
81+ switch ( severity ) {
82+ case 'CRITICAL' : return < AlertTriangle className = "text-red-500" /> ;
83+ case 'HIGH' : return < AlertCircle className = "text-orange-500" /> ;
84+ case 'MEDIUM' : return < AlertCircle className = "text-yellow-500" /> ;
85+ case 'LOW' : return < Info className = "text-blue-500" /> ;
86+ default : return < Info /> ;
87+ }
88+ } ;
89+
90+ // Helper function to consolidate findings by type
91+ const consolidateFindings = ( findings ) => {
92+ if ( ! findings || typeof findings !== 'object' ) {
93+ console . error ( 'Invalid findings structure' ) ;
94+ return { } ;
95+ }
96+
97+ const consolidated = { } ;
98+
99+ try {
100+ Object . entries ( findings ) . forEach ( ( [ category , subcategories ] ) => {
101+ if ( ! subcategories || typeof subcategories !== 'object' ) return ;
102+
103+ Object . entries ( subcategories ) . forEach ( ( [ subcategory , issues ] ) => {
104+ if ( ! Array . isArray ( issues ) ) return ;
105+
106+ issues . forEach ( issue => {
107+ if ( ! issue || ! issue . type ) return ;
108+
109+ const key = issue . type ;
110+ if ( ! consolidated [ key ] ) {
111+ consolidated [ key ] = {
112+ ...issue ,
113+ files : [ ] ,
114+ allLineNumbers : { }
115+ } ;
116+ }
117+ consolidated [ key ] . files . push ( issue . file ) ;
118+ consolidated [ key ] . allLineNumbers [ issue . file ] = issue . lineNumbers ;
119+ } ) ;
120+ } ) ;
121+ } ) ;
122+ } catch ( error ) {
123+ console . error ( 'Error consolidating findings:' , error ) ;
124+ }
125+
126+ return consolidated ;
68127 } ;
69128
70129 return (
@@ -93,7 +152,7 @@ const ScanResults = ({
93152
94153 { /* Summary Grid */ }
95154 < div className = "grid grid-cols-1 md:grid-cols-4 gap-4" >
96- { severityOrder . map ( severity => (
155+ { [ 'CRITICAL' , 'HIGH' , 'MEDIUM' , 'LOW' ] . map ( severity => (
97156 < div key = { severity } className = { `p-4 rounded-lg ${
98157 severity === 'CRITICAL' ? 'bg-red-100' :
99158 severity === 'HIGH' ? 'bg-orange-100' :
@@ -107,78 +166,87 @@ const ScanResults = ({
107166 ) ) }
108167 </ div >
109168
110- { /* Findings Sections */ }
111- < div className = "space-y-6" >
112- { severityOrder . map ( severity => {
113- const issuesList = findings [ severity ] || [ ] ;
114- if ( issuesList . length === 0 ) return null ;
115-
116- const Icon = severityIcons [ severity ] ;
117-
118- return (
119- < div key = { severity } className = "space-y-4" >
120- < h3 className = "text-lg font-semibold flex items-center" >
121- { Icon && < Icon className = "h-5 w-5 mr-2" /> }
122- { severity } Findings
123- < SeverityBadge severity = { severity } count = { issuesList . length } />
124- </ h3 >
125-
126- { issuesList . map ( ( issue , index ) => (
127- < div key = { `${ issue . type } -${ index } ` } className = "p-4 rounded-lg bg-white border" >
128- < div className = "space-y-2" >
129- < div className = "font-medium text-gray-900" > { issue . type } </ div >
130- < div className = "text-sm text-gray-600" > { issue . description } </ div >
131- < div className = "text-sm" >
132- < span className = "font-medium" > File: </ span >
133- < code className = "px-2 py-1 bg-gray-100 rounded" > { issue . file } </ code >
169+ { /* Consolidated Findings */ }
170+ < div className = "space-y-8" >
171+ { Object . entries ( consolidateFindings ( findings ) ) . map ( ( [ type , finding ] ) => (
172+ < div key = { type } className = "bg-white rounded-lg shadow p-6" >
173+ < div className = "flex items-start" >
174+ { getSeverityIcon ( finding . severity ) }
175+ < div className = "ml-3 flex-1" >
176+ < h3 className = "text-lg font-medium" >
177+ { type }
178+ { finding . subcategory && (
179+ < span className = "text-sm text-gray-500 ml-2" >
180+ CWE-{ finding . subcategory }
181+ </ span >
182+ ) }
183+ </ h3 >
184+ < p className = "text-gray-600 mt-1" > { finding . description } </ p >
185+
186+ { /* Affected Files */ }
187+ < div className = "mt-4 space-y-2" >
188+ { Object . entries ( finding . allLineNumbers ) . map ( ( [ file , lines ] ) => (
189+ < div key = { file } className = "text-sm" >
190+ < code className = "bg-gray-100 px-2 py-1 rounded" >
191+ { file }
192+ </ code >
193+ { lines ?. length > 0 && (
194+ < span className = "ml-2 text-gray-600" >
195+ Line{ lines . length > 1 ? 's' : '' } : { lines . join ( ', ' ) }
196+ </ span >
197+ ) }
134198 </ div >
135- { Array . isArray ( issue . lineNumbers ) && issue . lineNumbers . length > 0 && (
136- < div className = "text-sm" >
137- < span className = "font-medium" > Line{ issue . lineNumbers . length > 1 ? 's' : '' } : </ span >
138- < code className = "px-2 py-1 bg-gray-100 rounded" >
139- { issue . lineNumbers . join ( ', ' ) }
140- </ code >
141- </ div >
142- ) }
143- { issue . recommendation && (
144- < Alert className = "mt-2" >
145- < AlertDescription > { issue . recommendation } </ AlertDescription >
146- </ Alert >
147- ) }
148- </ div >
199+ ) ) }
149200 </ div >
150- ) ) }
201+
202+ { /* Recommendation */ }
203+ { finding . recommendation && (
204+ < Alert className = "mt-4" variant = "info" >
205+ < AlertDescription >
206+ { finding . recommendation }
207+ </ AlertDescription >
208+ </ Alert >
209+ ) }
210+ </ div >
151211 </ div >
152- ) ;
153- } ) }
212+ </ div >
213+ ) ) }
154214 </ div >
155215
156- { /* Recommended Fixes Section */ }
157- { recommendedFixes && recommendedFixes . length > 0 && (
158- < div className = "space-y-4" >
159- < h3 className = "text-lg font-semibold flex items-center" >
160- < CheckCircle className = "h-5 w-5 mr-2" />
161- Recommended Security Fixes
162- </ h3 >
163- { recommendedFixes . map ( ( fix , index ) => (
164- < div key = { index } className = "p-4 rounded-lg bg-white border" >
165- < div className = "space-y-3" >
166- < div className = "font-medium text-gray-900 flex items-center" >
167- < AlertTriangle className = "h-4 w-4 mr-2 text-orange-500" />
168- { fix . type }
169- </ div >
170- < div className = "text-sm space-y-2" >
171- < div className = "font-medium" > Mitigation Steps:</ div >
172- < div className = "text-gray-700" > { fix . recommendation ?. toString ( ) } </ div >
173- { fix . references && fix . references . length > 0 && (
216+ { /* References Section */ }
217+ { safeRecommendedFixes . length > 0 && (
218+ < div className = "bg-white rounded-lg shadow p-6" >
219+ < h2 className = "text-xl font-semibold mb-4" >
220+ Security References & Mitigation
221+ </ h2 >
222+ < div className = "space-y-4" >
223+ { Array . from ( new Set ( safeRecommendedFixes . map ( fix => fix . type ) ) ) . map ( type => {
224+ const fix = safeRecommendedFixes . find ( f => f . type === type ) ;
225+ return (
226+ < div key = { type } className = "border-t pt-4 first:border-t-0 first:pt-0" >
227+ < h3 className = "font-medium" >
228+ { type }
229+ { fix . cwe && (
230+ < a
231+ href = { `https://cwe.mitre.org/data/definitions/${ fix . cwe } .html` }
232+ target = "_blank"
233+ rel = "noopener noreferrer"
234+ className = "ml-2 text-sm text-blue-600 hover:text-blue-800"
235+ >
236+ (CWE-{ fix . cwe } )
237+ </ a >
238+ ) }
239+ </ h3 >
240+ < p className = "text-gray-600 mt-1" > { fix . recommendation } </ p >
241+ { fix . references ?. length > 0 && (
174242 < div className = "mt-2" >
175- < div className = "font-medium text-sm" > Security References :</ div >
176- < ul className = "list-disc pl-4 text-sm text-gray-600 space-y-1 " >
177- { fix . references ? .map ( ( ref , i ) => (
243+ < div className = "text-sm font-medium" > Additional Resources :</ div >
244+ < ul className = "list-disc pl-5 text-sm text-gray-600" >
245+ { fix . references . map ( ( ref , i ) => (
178246 < li key = { i } >
179247 < a
180- href = { ref . url }
181- target = "_blank"
248+ href = { ref . url }
249+ target = "_blank"
182250 rel = "noopener noreferrer"
183251 className = "text-blue-600 hover:text-blue-800"
184252 >
@@ -192,23 +260,10 @@ const ScanResults = ({
192260 </ ul >
193261 </ div >
194262 ) }
195- { fix . cwe && (
196- < div className = "mt-2" >
197- < div className = "font-medium text-sm" > CWE Reference:</ div >
198- < a
199- href = { `https://cwe.mitre.org/data/definitions/${ fix . cwe } .html` }
200- target = "_blank"
201- rel = "noopener noreferrer"
202- className = "text-sm text-blue-600 hover:text-blue-800"
203- >
204- CWE-{ fix . cwe ?. toString ( ) }
205- </ a >
206- </ div >
207- ) }
208263 </ div >
209- </ div >
210- </ div >
211- ) ) }
264+ ) ;
265+ } ) }
266+ </ div >
212267 </ div >
213268 ) }
214269
0 commit comments