Skip to content

Conversation

@cow-lang
Copy link
Contributor

ORT has a bug where license choice application is non-deterministic depending on the order of licenses in SPDX expressions. When "MIT OR Apache-2.0" appears in the scan result but the user configured "Apache-2.0 OR MIT", string matching fails, triggering a fallback that causes exponential expression growth.

Fixes: #10888

@cow-lang cow-lang requested a review from a team as a code owner November 18, 2025 13:47
@cow-lang cow-lang force-pushed the fix/10888-license-choice-order-independence branch from a1d9a52 to 3296ba8 Compare November 18, 2025 13:49
@codecov
Copy link

codecov bot commented Nov 18, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.45%. Comparing base (be610e3) to head (63242d9).
⚠️ Report is 14 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main   #11101      +/-   ##
============================================
+ Coverage     57.38%   57.45%   +0.06%     
- Complexity     1703     1708       +5     
============================================
  Files           346      346              
  Lines         12825    12844      +19     
  Branches       1214     1218       +4     
============================================
+ Hits           7360     7379      +19     
  Misses         4997     4997              
  Partials        468      468              
Flag Coverage Δ
funTest-no-external-tools 31.06% <ø> (-0.11%) ⬇️
test-ubuntu-24.04 42.46% <ø> (+<0.01%) ⬆️
test-windows-2025 42.44% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cow-lang cow-lang force-pushed the fix/10888-license-choice-order-independence branch 7 times, most recently from 52b1218 to 1666e6e Compare November 19, 2025 10:36
Copy link
Member

@sschuberth sschuberth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically LGTM, let me make some slight adjustments straight to your branch to get it merged soon.

Comment on lines 349 to 350
this != subExpression &&
children.any { it.isSubExpression(subExpression) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so these two lines seem to be the only different to the solution in my branch (for I created a draft PR now for easier comparison).

Comment on lines 202 to 239

"apply choice regardless of license order in OR expression" {
val expression = "(GPL-2.0-only OR MIT) AND Apache-2.0".toSpdx()

val choices1 = listOf(
SpdxLicenseChoice("GPL-2.0-only OR MIT".toSpdx(), "MIT".toSpdx())
)
val choices2 = listOf(
SpdxLicenseChoice("MIT OR GPL-2.0-only".toSpdx(), "MIT".toSpdx())
)

val result1 = expression.applyChoices(choices1)
result1 shouldBe "MIT AND Apache-2.0".toSpdx()

val result2 = expression.applyChoices(choices2)
result2 shouldBe "MIT AND Apache-2.0".toSpdx()
}

"resolve the choice for a sub-expression in a compound expression" {
// This test ensures that when applying a choice to a subexpression that appears multiple times,
// only the matching occurrence is replaced, not similar-looking expressions with WITH exceptions.
// This prevents the over-simplification bug where string replacement would affect too much.
val expression = "(CDDL-1.1 OR GPL-2.0-only) AND (CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0)"
.toSpdx()
val choices = listOf(
SpdxLicenseChoice("CDDL-1.1 OR GPL-2.0-only".toSpdx(), "CDDL-1.1".toSpdx())
)

val result = expression.applyChoices(choices)

// The second OR expression should remain unchanged because it has a WITH exception modifier
// and doesn't exactly match the subexpression being chosen.
result shouldBe "CDDL-1.1 AND (CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0)".toSpdx()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are new as well, but in exchange mine is omitted.

ORT had an architectural mismatch where `SpdxCompoundExpression` used
order-invariant equality ("MIT OR Apache-2.0" == "Apache-2.0 OR
MIT"), but the license choice matching used order-dependent matching
based on strings.

That is, if the expression contained "MIT OR Apache-2.0" but the user
gave "Apache-2.0 OR MIT", the string match failed, triggering a
fallback that rebuilt the expression by OR-ing all valid choices.
With multiple license choices, this caused exponential expression
growth (> 1000 characters in length).

This solution replaces the string-based algorithm with set-based
operations in `replaceSubexpressionWithChoice()`:

1. Decompose expressions into a set using `validChoices()`.
2. Remove dismissed choices from the set (no string operations).
3. Rebuild from remaining choices with controlled reduction.
4. Only recurse to AND-children if the sub-expression is actually
   contained in a child to prevent inappropriate recursion for the
   entire expression or derived sub-expression choices.

Since sets are order-invariant ({A,B} == {B,A}), both orderings
execute identical code paths, guaranteeing deterministic behavior.

Fixes oss-review-toolkit#10888.

Signed-off-by: Antoni <168914426+cow-lang@users.noreply.github.com>
Co-authored-by: Sebastian Schuberth <sebastian@doubleopen.org>
@sschuberth sschuberth force-pushed the fix/10888-license-choice-order-independence branch from 1666e6e to 63242d9 Compare November 19, 2025 16:54
@sschuberth sschuberth enabled auto-merge (rebase) November 19, 2025 16:55
@sschuberth sschuberth mentioned this pull request Nov 19, 2025
@sschuberth sschuberth disabled auto-merge November 19, 2025 17:17
@sschuberth
Copy link
Member

Merging despite the unrelated test failure(s).

@sschuberth sschuberth merged commit 41a3e69 into oss-review-toolkit:main Nov 19, 2025
24 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Problems with commutative OR in SPDX expressions

2 participants