Skip to content

fix(license): treat UNKNOWN-category dependencies as incompatible#513

Merged
a-oren merged 4 commits into
guacsec:mainfrom
a-oren:TC-4707
Jun 11, 2026
Merged

fix(license): treat UNKNOWN-category dependencies as incompatible#513
a-oren merged 4 commits into
guacsec:mainfrom
a-oren:TC-4707

Conversation

@a-oren

@a-oren a-oren commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Split the UNKNOWN category check in getCompatibility() so dependencies with unrecognized licenses are flagged as incompatible when the project has a known license category
  • Added distinct reason message for UNKNOWN-category dependencies: "License not recognized as a standard SPDX identifier. Manual review recommended to verify compatibility."
  • Added parameterized tests covering all UNKNOWN/known category combinations and preserving existing behavior

Implements TC-4707

Test plan

  • getCompatibility(PERMISSIVE|WEAK_COPYLEFT|STRONG_COPYLEFT, UNKNOWN) returns INCOMPATIBLE
  • getCompatibility(UNKNOWN, *) returns UNKNOWN (no false positives)
  • getCompatibility(null, *) returns UNKNOWN
  • Known category pairs preserve existing behavior
  • All 454 existing tests pass (6 pre-existing Python env failures unrelated)

Summary by Sourcery

Treat dependencies with UNKNOWN license category as incompatible when the project license category is known, and clarify reporting for such cases.

Bug Fixes:

  • Ensure UNKNOWN-category dependency licenses are reported as incompatible when the project has a known license category.

Enhancements:

  • Add a specific incompatibility reason message for dependencies with UNKNOWN license category in license check results.

Tests:

  • Introduce parameterized tests covering UNKNOWN vs known license category combinations and verifying existing compatibility behavior for known categories.

Split the UNKNOWN category check in getCompatibility() so that
dependencies with unrecognized licenses are flagged as incompatible
when the project has a known license category, while keeping UNKNOWN
project category returning UNKNOWN (no false positives). Add a distinct
reason message for UNKNOWN-category dependencies to guide manual review.

Implements TC-4707

Assisted-by: Claude Code
@sourcery-ai

sourcery-ai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adjusts license compatibility logic so UNKNOWN dependency categories are treated as incompatible when the project license category is known, adds a specific user-facing reason message for these cases, and introduces parameterized tests to lock in behavior for UNKNOWN and known category combinations.

Sequence diagram for handling UNKNOWN dependency license category

sequenceDiagram
  participant LicenseCheck
  participant LicenseUtils
  participant IncompatibleDependency

  LicenseCheck->>LicenseUtils: getCompatibility(projectCategory, entry.category)
  LicenseUtils-->>LicenseCheck: Compatibility.INCOMPATIBLE
  alt entry.category == LicenseCategory.UNKNOWN
    LicenseCheck->>IncompatibleDependency: new IncompatibleDependency(purl, entry.licenses, entry.category, reason)
    Note right of IncompatibleDependency: License not recognized as a standard SPDX identifier. Manual review recommended to verify compatibility.
  else entry.category != LicenseCategory.UNKNOWN
    LicenseCheck->>IncompatibleDependency: new IncompatibleDependency(purl, entry.licenses, entry.category, reason)
    Note right of IncompatibleDependency: Dependency license(s) are incompatible with the project license.
  end
Loading

Flow diagram for updated license compatibility evaluation

flowchart TD
  A[Start getCompatibility
  projectCategory, dependencyCategory] --> B{projectCategory == null
  or dependencyCategory == null}
  B -->|yes| C[Return Compatibility.UNKNOWN]
  B -->|no| D{projectCategory == LicenseCategory.UNKNOWN}
  D -->|yes| C
  D -->|no| E{dependencyCategory == LicenseCategory.UNKNOWN}
  E -->|yes| F[Return Compatibility.INCOMPATIBLE]
  E -->|no| G[Compute restrictiveness
  for projectCategory and dependencyCategory]
  G --> H[Return Compatibility based on
  restrictiveness comparison]
Loading

File-Level Changes

Change Details Files
Treat UNKNOWN dependency license category as incompatible when project category is known, and surface a tailored reason message.
  • Refined getCompatibility() to return UNKNOWN only when the project category is UNKNOWN (or null) and INCOMPATIBLE when the dependency category is UNKNOWN while the project category is known.
  • Preserved existing comparison-based compatibility logic for known project/dependency category pairs after the UNKNOWN handling short-circuit.
  • Adjusted LicenseCheck.runLicenseCheck() to construct a specialized reason string for UNKNOWN-category dependencies, while keeping the existing generic incompatibility message for all other incompatible cases.
src/main/java/io/github/guacsec/trustifyda/license/LicenseUtils.java
src/main/java/io/github/guacsec/trustifyda/license/LicenseCheck.java
Add focused, parameterized tests to pin behavior of UNKNOWN and known license category combinations without regressing current compatibility rules.
  • Introduced License_Compatibility_Test with nested test classes separating UNKNOWN dependency, UNKNOWN/null project, and known-category scenarios.
  • Added parameterized tests ensuring known project + UNKNOWN dependency yields INCOMPATIBLE, UNKNOWN project yields UNKNOWN for all dependency categories, and null project yields UNKNOWN for all dependency categories.
  • Defined a knownCategoryPairs() MethodSource enumerating representative known-category combinations to verify existing compatibility semantics for equal, less restrictive, and more restrictive dependency categories remain unchanged.
src/test/java/io/github/guacsec/trustifyda/license/License_Compatibility_Test.java

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • Consider centralizing the construction of the incompatibility reason message (e.g., in LicenseUtils or a small helper) so that future callers of getCompatibility don’t have to re‑encode the UNKNOWN vs non‑UNKNOWN branching logic and texts independently.
  • If LicenseCategory.UNKNOWN can be produced for reasons other than a non‑SPDX identifier, you may want to either adjust the wording of the new reason string or have the code receive a more specific cause from the classification layer to avoid over‑specifying the root cause here.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider centralizing the construction of the incompatibility reason message (e.g., in `LicenseUtils` or a small helper) so that future callers of `getCompatibility` don’t have to re‑encode the UNKNOWN vs non‑UNKNOWN branching logic and texts independently.
- If `LicenseCategory.UNKNOWN` can be produced for reasons other than a non‑SPDX identifier, you may want to either adjust the wording of the new reason string or have the code receive a more specific cause from the classification layer to avoid over‑specifying the root cause here.

## Individual Comments

### Comment 1
<location path="src/test/java/io/github/guacsec/trustifyda/license/License_Compatibility_Test.java" line_range="67-64" />
<code_context>
+      assertThat(result).isEqualTo(Compatibility.UNKNOWN);
+    }
+
+    /** Null project category should return UNKNOWN. */
+    @ParameterizedTest
+    @EnumSource(LicenseCategory.class)
+    void null_project_returns_unknown(LicenseCategory dependencyCategory) {
+      // When
+      Compatibility result = LicenseUtils.getCompatibility(null, dependencyCategory);
+
+      // Then
+      assertThat(result).isEqualTo(Compatibility.UNKNOWN);
+    }
+  }
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests for null dependency category to fully cover the null-handling branch

The implementation returns `Compatibility.UNKNOWN` when either `projectCategory` or `dependencyCategory` is `null`, but tests only cover the `null` project case. Please also add parameterized tests for:

- `getCompatibility(projectCategory, null)` for each non-null project category (including `UNKNOWN`).
- `getCompatibility(null, null)` explicitly.

This will fully cover the null-handling branch and protect against regressions.

Suggested implementation:

```java
    /** Null project category should return UNKNOWN. */
    @ParameterizedTest
    @EnumSource(LicenseCategory.class)
    void null_project_returns_unknown(LicenseCategory dependencyCategory) {
      // When
      Compatibility result = LicenseUtils.getCompatibility(null, dependencyCategory);

      // Then
      assertThat(result).isEqualTo(Compatibility.UNKNOWN);
    }

    /** Null dependency category should return UNKNOWN. */
    @ParameterizedTest
    @EnumSource(LicenseCategory.class)
    void null_dependency_returns_unknown(LicenseCategory projectCategory) {
      // When
      Compatibility result = LicenseUtils.getCompatibility(projectCategory, null);

      // Then
      assertThat(result).isEqualTo(Compatibility.UNKNOWN);
    }

    /** Null project and dependency categories should return UNKNOWN. */
    @Test
    void null_project_and_dependency_return_unknown() {
      // When
      Compatibility result = LicenseUtils.getCompatibility(null, null);

      // Then
      assertThat(result).isEqualTo(Compatibility.UNKNOWN);
    }
  }

```

If `@Test` is not already imported in this test class, add:

`import org.junit.jupiter.api.Test;`

near the other JUnit imports.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@a-oren a-oren requested review from Strum355 and removed request for Strum355 June 4, 2026 18:03
@codecov-commenter

codecov-commenter commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 42.85714% with 4 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@1fba9c2). Learn more about missing BASE report.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #513   +/-   ##
=======================================
  Coverage        ?   68.68%           
  Complexity      ?     1000           
=======================================
  Files           ?       65           
  Lines           ?     4244           
  Branches        ?      743           
=======================================
  Hits            ?     2915           
  Misses          ?      990           
  Partials        ?      339           
Flag Coverage Δ
integration-tests 68.68% <42.85%> (?)

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@a-oren

a-oren commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Verification Report for TC-4707 (commit 968c146)

Check Result Details
Review Feedback N/A 1 bot suggestion (sourcery-ai), no code change requests
Root-Cause Investigation N/A No sub-tasks created
Scope Containment PASS All 3 PR files match task specification exactly
Diff Size PASS Proportionate to task scope (2 modified + 1 new test file)
Commit Traceability PASS Primary commit references TC-4707
Sensitive Patterns PASS No secrets or credentials detected
CI Status PASS All 44 CI checks pass
Acceptance Criteria PASS 5 of 5 criteria met
Test Quality PASS Tests well-parameterized, all documented; Eval Quality: N/A
Test Change Classification ADDITIVE New test file adds 20 parameterized test cases
Verification Commands PASS All tests pass (mvn test)

Overall: PASS

All checks pass. The PR correctly splits the UNKNOWN category handling so that dependencies with unrecognized licenses are flagged as incompatible when the project has a known license, while preserving UNKNOWN return for unknown project categories. Test coverage is thorough with parameterized tests covering all acceptance criteria and test requirements.


This comment was AI-generated by sdlc-workflow/verify-pr v0.9.2.

@a-oren a-oren requested a review from Strum355 June 11, 2026 09:45

@ruromero ruromero left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Only one minor suggestion

Comment thread src/main/java/io/github/guacsec/trustifyda/license/LicenseCheck.java Outdated
UNKNOWN category can result from categorization issues, not just
non-standard SPDX identifiers. Use "License category could not be
determined" instead of "License not recognized as a standard SPDX
identifier" per reviewer feedback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@a-oren a-oren requested a review from ruromero June 11, 2026 14:26
@a-oren a-oren merged commit d1d6800 into guacsec:main Jun 11, 2026
45 checks passed
@a-oren a-oren deleted the TC-4707 branch June 11, 2026 14:42
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.

3 participants