@@ -183,7 +183,8 @@ private DisplayPackageViewModel SetupCommon(
183183 }
184184 }
185185
186- if ( packageKeyToDeprecation != null && packageKeyToDeprecation . TryGetValue ( package . Key , out var deprecation ) )
186+ PackageDeprecation deprecation = null ;
187+ if ( packageKeyToDeprecation != null && packageKeyToDeprecation . TryGetValue ( package . Key , out deprecation ) )
187188 {
188189 viewModel . DeprecationStatus = deprecation . Status ;
189190 }
@@ -192,15 +193,75 @@ private DisplayPackageViewModel SetupCommon(
192193 viewModel . DeprecationStatus = PackageDeprecationStatus . NotDeprecated ;
193194 }
194195
195- if ( packageKeyToVulnerabilities != null && packageKeyToVulnerabilities . TryGetValue ( package . Key , out var vulnerabilities ) )
196+ PackageVulnerabilitySeverity ? maxVulnerabilitySeverity = null ;
197+ if ( packageKeyToVulnerabilities != null
198+ && packageKeyToVulnerabilities . TryGetValue ( package . Key , out var vulnerabilities )
199+ && vulnerabilities != null && vulnerabilities . Any ( ) )
196200 {
197201 viewModel . Vulnerabilities = vulnerabilities ;
198- viewModel . MaxVulnerabilitySeverity = vulnerabilities . Max ( v => v . Severity ) ;
202+ maxVulnerabilitySeverity = viewModel . Vulnerabilities . Max ( v => v . Severity ) ; // cache for messaging
203+ viewModel . MaxVulnerabilitySeverity = maxVulnerabilitySeverity . Value ;
204+ }
205+ else
206+ {
207+ viewModel . Vulnerabilities = null ;
208+ viewModel . MaxVulnerabilitySeverity = default ;
199209 }
200210
211+ viewModel . PackageWarningIconTitle =
212+ GetWarningIconTitle ( viewModel . Version , deprecation , maxVulnerabilitySeverity ) ;
213+
201214 return viewModel ;
202215 }
203216
217+ private static string GetWarningIconTitle (
218+ string version ,
219+ PackageDeprecation deprecation ,
220+ PackageVulnerabilitySeverity ? maxVulnerabilitySeverity )
221+ {
222+ // We want a tooltip title for the warning icon, which concatenates deprecation and vulnerability information cleanly
223+ var deprecationTitle = "" ;
224+ if ( deprecation != null )
225+ {
226+ deprecationTitle = version ;
227+ var isLegacy = deprecation . Status . HasFlag ( PackageDeprecationStatus . Legacy ) ;
228+ var hasCriticalBugs = deprecation . Status . HasFlag ( PackageDeprecationStatus . CriticalBugs ) ;
229+ if ( hasCriticalBugs )
230+ {
231+ if ( isLegacy )
232+ {
233+ deprecationTitle += " is deprecated because it's legacy and has critical bugs" ;
234+ }
235+ else
236+ {
237+ deprecationTitle += " is deprecated because it has critical bugs" ;
238+ }
239+ }
240+ else if ( isLegacy )
241+ {
242+ deprecationTitle += " is deprecated because it's legacy and no longer maintained" ;
243+ }
244+ else
245+ {
246+ deprecationTitle += " is deprecated" ;
247+ }
248+ }
249+
250+ if ( maxVulnerabilitySeverity . HasValue )
251+ {
252+ var severity = Enum . GetName ( typeof ( PackageVulnerabilitySeverity ) , maxVulnerabilitySeverity ) ? . ToLowerInvariant ( ) ?? "unknown" ;
253+ var vulnerabilitiesTitle = $ "{ version } has at least one vulnerability with { severity } severity.";
254+
255+ return string . IsNullOrEmpty ( deprecationTitle )
256+ ? vulnerabilitiesTitle
257+ : $ "{ deprecationTitle } ; { vulnerabilitiesTitle } ";
258+ }
259+
260+ return string . IsNullOrEmpty ( deprecationTitle )
261+ ? string . Empty
262+ : $ "{ deprecationTitle } .";
263+ }
264+
204265 private static string GetPushedBy ( Package package , User currentUser , Dictionary < User , string > pushedByCache )
205266 {
206267 var userPushedBy = package . User ;
0 commit comments