55using System . Collections . Generic ;
66using System . Linq ;
77using System . Net ;
8+ using System . Text . RegularExpressions ;
89using System . Threading . Tasks ;
910using System . Web . Mvc ;
1011using NuGet . Services . Entities ;
@@ -16,6 +17,15 @@ namespace NuGetGallery
1617 public partial class ManageDeprecationJsonApiController
1718 : AppController
1819 {
20+ private static readonly TimeSpan RegexTimeout = TimeSpan . FromMinutes ( 1 ) ;
21+
22+ public const string CveIdRegexYearGroupName = "year" ;
23+ public const string CveIdRegexPattern = @"CVE-(?<" + CveIdRegexYearGroupName + @">\d{4})-\d{4,}" ;
24+ private static readonly Regex CveIdRegex = GetRegexFromPattern ( CveIdRegexPattern ) ;
25+
26+ public const string CweIdRegexPattern = @"CWE-\d+" ;
27+ private static readonly Regex CweIdRegex = GetRegexFromPattern ( CweIdRegexPattern ) ;
28+
1929 private readonly IVulnerabilityAutocompleteService _vulnerabilityAutocompleteService ;
2030 private readonly IPackageService _packageService ;
2131 private readonly IPackageDeprecationService _deprecationService ;
@@ -109,6 +119,28 @@ public virtual async Task<JsonResult> Deprecate(
109119 return DeprecateErrorResponse ( HttpStatusCode . BadRequest , Strings . DeprecatePackage_NoVersions ) ;
110120 }
111121
122+ JsonResult vulnerabilityDetailIdsErrorResult ;
123+
124+ cveIds = cveIds ?? Enumerable . Empty < string > ( ) ;
125+ if ( ! TryVerifyVulnerabilityDetailIds (
126+ cveIds ,
127+ IsValidCveId ,
128+ Strings . DeprecatePackage_InvalidCve ,
129+ out vulnerabilityDetailIdsErrorResult ) )
130+ {
131+ return vulnerabilityDetailIdsErrorResult ;
132+ }
133+
134+ cweIds = cweIds ?? Enumerable . Empty < string > ( ) ;
135+ if ( ! TryVerifyVulnerabilityDetailIds (
136+ cweIds ,
137+ IsValidCweId ,
138+ Strings . DeprecatePackage_InvalidCwe ,
139+ out vulnerabilityDetailIdsErrorResult ) )
140+ {
141+ return vulnerabilityDetailIdsErrorResult ;
142+ }
143+
112144 if ( cvssRating . HasValue && ( cvssRating < 0 || cvssRating > 10 ) )
113145 {
114146 return DeprecateErrorResponse ( HttpStatusCode . BadRequest , Strings . DeprecatePackage_InvalidCvss ) ;
@@ -180,18 +212,16 @@ public virtual async Task<JsonResult> Deprecate(
180212 }
181213 }
182214
183- cveIds = cveIds ?? Enumerable . Empty < string > ( ) ;
184- var cves = _deprecationService . GetCvesById ( cveIds ) ;
185- if ( cveIds . Count ( ) != cves . Count )
215+ var cves = await _deprecationService . GetOrCreateCvesByIdAsync ( cveIds , commitChanges : false ) ;
216+
217+ IReadOnlyCollection < Cwe > cwes ;
218+ try
186219 {
187- return DeprecateErrorResponse ( HttpStatusCode . NotFound , Strings . DeprecatePackage_MissingCve ) ;
220+ cwes = _deprecationService . GetCwesById ( cweIds ) ;
188221 }
189-
190- cweIds = cweIds ?? Enumerable . Empty < string > ( ) ;
191- var cwes = _deprecationService . GetCwesById ( cweIds ) ;
192- if ( cweIds . Count ( ) != cwes . Count )
222+ catch ( ArgumentException )
193223 {
194- return DeprecateErrorResponse ( HttpStatusCode . NotFound , Strings . DeprecatePackage_MissingCwe ) ;
224+ return DeprecateErrorResponse ( HttpStatusCode . NotFound , Strings . DeprecatePackage_CweMissing ) ;
195225 }
196226
197227 var status = PackageDeprecationStatus . NotDeprecated ;
@@ -228,5 +258,66 @@ private JsonResult DeprecateErrorResponse(HttpStatusCode code, string error)
228258 {
229259 return Json ( code , new { error } ) ;
230260 }
261+
262+ /// <summary>
263+ /// Verifies IDs in the list match <paramref name="regex"/>.
264+ /// If they don't, returns <c>false</c> and sets <paramref name="result"/> to the expected <see cref="JsonResult"/>.
265+ /// Otherwise, returns <c>true</c>.
266+ /// </summary>
267+ /// <param name="errorString">The error string to use to construct <paramref name="result"/>.</param>
268+ private bool TryVerifyVulnerabilityDetailIds (
269+ IEnumerable < string > ids ,
270+ Func < string , bool > isValid ,
271+ string errorString ,
272+ out JsonResult result )
273+ {
274+ result = null ;
275+ string invalidId ;
276+ if ( ( invalidId = ids . FirstOrDefault ( c => ! isValid ( c ) ) ) != null )
277+ {
278+ result = DeprecateErrorResponse (
279+ HttpStatusCode . BadRequest ,
280+ string . Format ( errorString , invalidId ) ) ;
281+
282+ return false ;
283+ }
284+
285+ return true ;
286+ }
287+
288+ private bool IsValidCveId ( string id )
289+ {
290+ var match = CveIdRegex . Match ( id ) ;
291+ if ( match . Value == string . Empty )
292+ {
293+ return false ;
294+ }
295+
296+ var yearString = match . Groups [ CveIdRegexYearGroupName ] . Value ;
297+ if ( ! int . TryParse ( yearString . ToString ( ) , out var year ) )
298+ {
299+ return false ;
300+ }
301+
302+ if ( year < 1999 || year > DateTime . UtcNow . Year )
303+ {
304+ return false ;
305+ }
306+
307+ return true ;
308+ }
309+
310+ private bool IsValidCweId ( string id )
311+ {
312+ return CweIdRegex . IsMatch ( id ) ;
313+ }
314+
315+ private static Regex GetRegexFromPattern ( string pattern )
316+ {
317+ return new Regex (
318+ pattern ,
319+ RegexOptions . Compiled | RegexOptions . IgnoreCase | RegexOptions . Singleline ,
320+ RegexTimeout ) ;
321+ }
231322 }
232323}
0 commit comments