Skip to content

Commit

Permalink
Bugfix/cyclonedx reader license expression (#280)
Browse files Browse the repository at this point in the history
* Add "packageType" parameter and logic to create purl in CSV Reader. Refactor tests and config readers

* Add warn message for unknown packagetypes

* Improve log message

* Add release note

* Add Unit Test and format code

* Add documentation

* Update CSV reader doc

* Move switch-case block to dedicated method

* Run tests with packageType=null

* Check for null or empty packageType. Make logger non static for testing purposes.

* Add mockito dependency for tests.

* Add tests for npm, pypi and empty packageType

* minor improvement

* swap position of artifactId and version in config

* formatting

* Consistent syntax

* Add condition to check for expressions

* Take expression as it is instead of parsing and splitting the licenses.

* Add release note

* Remove unused imports

* Add unit test for reading an expression

---------

Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com>
  • Loading branch information
duph97 and ohecker authored Jul 26, 2024
1 parent 6276bfa commit 5ddce94
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,29 +115,37 @@ public void readInventory(String type, String sourceUrl, Application application
else if (licensesNode != null && licensesNode.isEmpty()) {
addRawLicense(appComponent, null, null, sourceUrl);
}
// Case if licenses field exists and contains licenses
// Case if licenses field exists and contains expressions or licenses
else if (licensesNode != null && licensesNode.isEmpty() == false) {
// Iterate over each "license" object within the "licenses" array
for (JsonNode licenseNode : licensesNode) {
// Declared License can be written either in "id" or "name" field. Prefer "id" as its written in SPDX
// format.
if (licenseNode.get("license").has("id")) {
if (licenseNode.get("license").has("url")) {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("license").get("id").asText(),
licenseNode.get("license").get("url").asText(), sourceUrl);
} else {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("license").get("id").asText(), null, sourceUrl);
}
} else if (licenseNode.get("license").has("name")) {
if (licenseNode.get("license").has("url")) {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("license").get("name").asText(),
licenseNode.get("license").get("url").asText(), sourceUrl);
} else {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("license").get("name").asText(), null, sourceUrl);
// Check for expressions
if (licenseNode.has("expression")) {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("expression").asText(), null, sourceUrl);
}

// Check for licenses
if (licenseNode.has("license")) {
// Declared License can be written either in "id" or "name" field. Prefer "id" as its written in SPDX
// format.
if (licenseNode.get("license").has("id")) {
if (licenseNode.get("license").has("url")) {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("license").get("id").asText(),
licenseNode.get("license").get("url").asText(), sourceUrl);
} else {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("license").get("id").asText(), null, sourceUrl);
}
} else if (licenseNode.get("license").has("name")) {
if (licenseNode.get("license").has("url")) {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("license").get("name").asText(),
licenseNode.get("license").get("url").asText(), sourceUrl);
} else {
licenseCount++;
addRawLicense(appComponent, licenseNode.get("license").get("name").asText(), null, sourceUrl);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,34 @@ public void readNpmFileAndCheckSize() {
}
assertTrue(found);
}

/**
* Test the {@link CyclonedxReader#readInventory()} method. Input file is an SBOM containing expressions.
*/
@Test
public void readExpression() {

// Always return a non-empty String for npm purls
Mockito.when(this.delegatingPurlHandler.pathFor(Mockito.startsWith("pkg:maven/"))).thenReturn("foo");

Application application = this.modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com",
"Angular");
this.cdxr.setModelFactory(this.modelFactory);
this.cdxr.setInputStreamFactory(new FileInputStreamFactory());
this.cdxr.setDelegatingPackageURLHandler(this.delegatingPurlHandler);
this.cdxr.readInventory("npm", "src/test/resources/expressionsbom.json", application, UsagePattern.DYNAMIC_LINKING,
"cyclonedx", null, null);
LOG.info(application.toString());

boolean found = false;

for (ApplicationComponent ap : application.getApplicationComponents()) {
if (ap.getArtifactId().equals("hk2-locator") && ap.getVersion().equals("2.5.0-b42")) {
found = true;
assertEquals("(CDDL-1.0 OR GPL-2.0-with-classpath-exception)", ap.getRawLicenses().get(0).getDeclaredLicense());

}
}
assertTrue(found);
}
}
84 changes: 84 additions & 0 deletions core/src/test/resources/expressionsbom.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:dd714b36-700a-4a2c-be85-eeb8723c2489",
"version": 1,
"metadata": {
"timestamp": "2024-07-08T17:39:55Z",
"tools": {
"components": [
{
"group": "@cyclonedx",
"name": "cdxgen",
"version": "10.7.1",
"purl": "pkg:npm/%40cyclonedx/cdxgen@10.7.1",
"type": "application",
"bom-ref": "pkg:npm/@cyclonedx/cdxgen@10.7.1",
"author": "OWASP Foundation",
"publisher": "OWASP Foundation"
}
]
},
"authors": [
{
"name": "OWASP Foundation"
}
],
"lifecycles": [
{
"phase": "build"
}
],
"component": {
"group": "",
"name": "lib",
"version": "latest",
"type": "application",
"bom-ref": "pkg:maven/lib@latest",
"purl": "pkg:maven/lib@latest"
}
},
"components": [
{
"publisher": "Oracle Corporation",
"group": "org.glassfish.hk2",
"name": "hk2-locator",
"version": "2.5.0-b42",
"description": "${project.name}",
"licenses": [
{
"expression": "(CDDL-1.0 OR GPL-2.0-with-classpath-exception)"
}
],
"purl": "pkg:maven/org.glassfish.hk2/hk2-locator@2.5.0-b42?type=jar",
"externalReferences": [
{
"type": "vcs",
"url": "https://hk2-project.github.io"
}
],
"type": "library",
"bom-ref": "pkg:maven/org.glassfish.hk2/hk2-locator@2.5.0-b42?type=jar",
"evidence": {
"identity": {
"field": "purl",
"confidence": 1,
"methods": [
{
"technique": "manifest-analysis",
"confidence": 1,
"value": "hk2-locator-2.5.0-b42.jar"
}
]
}
},
"properties": [
{
"name": "SrcFile",
"value": "hk2-locator-2.5.0-b42.jar"
}
]
}
],
"dependencies": []
}
1 change: 1 addition & 0 deletions documentation/master-solicitor.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -1901,6 +1901,7 @@ Spring beans implementing this interface will be called at certain points in the
Changes in 1.25.0::
* https://github.com/devonfw/solicitor/issues/277: When reading content (license texts or notice files) within the scancode adapter files which are greater than 1 million bytes will be skipped. This avoids large memory consumption and resulting instability.
* https://github.com/devonfw/solicitor/issues/274: Fixed issue where no packageURL was created when using the CSV reader. Added attribute 'packageType'.
* https://github.com/devonfw/solicitor/issues/279: Fixed issue where the CycloneDX reader could not read licenses declared as 'expression'.

Changes in 1.24.2::
* https://github.com/devonfw/solicitor/pull/271: Fixed an incompatibility with JDK 8.
Expand Down

0 comments on commit 5ddce94

Please sign in to comment.