Skip to content

Commit 53e12f5

Browse files
committed
Improve license expression parsing
1 parent 7fdb0d7 commit 53e12f5

1 file changed

Lines changed: 25 additions & 10 deletions

File tree

Runner/Helpers/NuGetClient.cs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using NuGet.Frameworks;
66
using NuGet.Packaging;
77
using NuGet.Packaging.Core;
8+
using NuGet.Packaging.Licenses;
89
using NuGet.Protocol;
910
using NuGet.Protocol.Core.Types;
1011
using NuGet.Resolver;
@@ -145,18 +146,32 @@ public static bool IsPermissiveLicense(string? expression)
145146
if (string.IsNullOrWhiteSpace(expression))
146147
return false;
147148

148-
// Strip exception clauses: "Apache-2.0 WITH LLVM-exception" -> "Apache-2.0"
149-
expression = Regex.Replace(expression, @"\bWITH\s+\S+", "", RegexOptions.IgnoreCase);
149+
try
150+
{
151+
return IsPermissive(NuGetLicenseExpression.Parse(expression));
152+
}
153+
catch (NuGetLicenseExpressionParsingException)
154+
{
155+
return false;
156+
}
150157

151-
// Extract license identifiers, skipping operators and parens
152-
var licenses = Regex.Split(expression, @"[\s()]+")
153-
.Where(t => !string.IsNullOrWhiteSpace(t))
154-
.Where(t => !t.Equals("OR", StringComparison.OrdinalIgnoreCase))
155-
.Where(t => !t.Equals("AND", StringComparison.OrdinalIgnoreCase))
156-
.Select(t => t.TrimEnd('+'))
157-
.ToList();
158+
static bool IsPermissive(NuGetLicenseExpression expression)
159+
{
160+
if (!System.Runtime.CompilerServices.RuntimeHelpers.TryEnsureSufficientExecutionStack())
161+
return false;
158162

159-
return licenses.Count > 0 && licenses.All(l => s_permissiveLicenses.Contains(l));
163+
return expression switch
164+
{
165+
// A leaf license is permissive if its SPDX identifier is in the allow-list.
166+
NuGetLicense license => s_permissiveLicenses.Contains(license.Identifier),
167+
// "license WITH exception" only grants additional rights, so judge by the base license.
168+
WithOperator withOperator => IsPermissive(withOperator.License),
169+
// "A AND B" requires complying with both; "A OR B" lets the consumer pick either.
170+
LogicalOperator { LogicalOperatorType: LogicalOperatorType.And } and => IsPermissive(and.Left) && IsPermissive(and.Right),
171+
LogicalOperator { LogicalOperatorType: LogicalOperatorType.Or } or => IsPermissive(or.Left) || IsPermissive(or.Right),
172+
_ => false,
173+
};
174+
}
160175
}
161176

162177
// === Version resolution ===

0 commit comments

Comments
 (0)