From 29ad70bd7fb4ad7f9c0df4a754ee799c027a0412 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 2 Jun 2022 22:47:30 +0200 Subject: [PATCH 001/139] Experimental scancode integration (#120) --- core/pom.xml | 9 + .../tools/solicitor/InventoryProcessor.java | 47 +++ .../devonfw/tools/solicitor/Solicitor.java | 18 +- .../solicitor/common/LicenseTextHelper.java | 32 ++ .../tools/solicitor/common/LogMessages.java | 12 +- .../common/ResourceToFileCopier.java | 4 +- .../AbstractSingleKindPackageURLHandler.java | 6 +- .../tools/solicitor/config/RuleConfig.java | 45 +++ .../model/ModelImporterExporter.java | 9 + .../model/impl/ModelFactoryImpl.java | 5 +- .../inventory/ApplicationComponentImpl.java | 80 ++++- .../impl/inventory/NormalizedLicenseImpl.java | 31 +- .../model/impl/inventory/RawLicenseImpl.java | 20 +- .../model/inventory/ApplicationComponent.java | 35 +++ .../model/inventory/NormalizedLicense.java | 7 + .../solicitor/model/inventory/RawLicense.java | 14 + .../solicitor/reader/AbstractReader.java | 3 +- .../solicitor/reader/yarn/YarnReader.java | 6 +- .../solicitor/ruleengine/RuleEngine.java | 11 +- .../ruleengine/drools/DroolsRuleEngine.java | 14 +- .../scancode/ComponentScancodeInfos.java | 227 +++++++++++++++ .../solicitor/scancode/ScancodeAdapter.java | 21 ++ .../solicitor/scancode/ScancodeException.java | 36 +++ .../scancode/ScancodeFileAdapter.java | 273 ++++++++++++++++++ .../scancode/ScancodeInventoryProcessor.java | 193 +++++++++++++ .../solicitor/writer/velocity/EscapeTool.java | 30 ++ .../writer/velocity/VelocityWriter.java | 1 - .../src/main/resources/application.properties | 14 + .../tools/solicitor/config/solicitor_base.cfg | 28 +- ...mple.xls => LicenseAssignmentV2Sample.xls} | Bin 60928 -> 61440 bytes .../rule_templates/LicenseAssignmentV2.drt | 118 ++++++++ ...ncomponents_with_noncommerciallicenses.sql | 26 ++ ...th_noncommerciallicenses_with_licenses.sql | 31 ++ .../tools/solicitor/sql/noticefiles.sql | 20 ++ .../sql/ossapplicationcomponents.sql | 4 +- .../sql/ossden_normalizedlicenses.sql | 29 ++ .../tools/solicitor/sql/scancode_sources.sql | 3 +- ...uelicenses_with_application_components.sql | 21 ++ .../tools/solicitor/templates/Attributions.vm | 163 +++++++++++ .../solicitor/templates/ScancodeScanScript.vm | 17 +- .../solicitor/templates/ScancodeScript.vm | 6 +- ...ect.xls => LicenseAssignmentV2Project.xls} | Bin 32768 -> 32768 bytes .../impl/MavenPackageURLHandlerTests.java | 10 + .../test/resources/scancode/curations.json | 2 + documentation/files/application.properties | 14 + documentation/files/solicitor_base.cfg | 52 +++- documentation/master-solicitor.asciidoc | 179 +++++++++++- 47 files changed, 1842 insertions(+), 84 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/EscapeTool.java rename core/src/main/resources/com/devonfw/tools/solicitor/rules/{LicenseAssignmentSample.xls => LicenseAssignmentV2Sample.xls} (81%) create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/ossden_normalizedlicenses.sql create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql create mode 100644 core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm rename core/src/main/resources/starters/rules/{LicenseAssignmentProject.xls => LicenseAssignmentV2Project.xls} (79%) create mode 100644 core/src/test/resources/scancode/curations.json diff --git a/core/pom.xml b/core/pom.xml index 781f8384..5376ce74 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -53,6 +53,10 @@ com.fasterxml.jackson.core jackson-databind + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + org.springframework.boot spring-boot-starter-data-jpa @@ -92,6 +96,11 @@ commons-csv 1.9.0 + + commons-codec + commons-codec + 1.15 + org.apache.velocity velocity-engine-core diff --git a/core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java new file mode 100644 index 00000000..b2e3af3c --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java @@ -0,0 +1,47 @@ +package com.devonfw.tools.solicitor; + +import org.springframework.core.annotation.Order; + +import com.devonfw.tools.solicitor.model.ModelRoot; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.inventory.RawLicense; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.Engagement; +import com.devonfw.tools.solicitor.reader.Reader; +import com.devonfw.tools.solicitor.ruleengine.RuleEngine; +import com.devonfw.tools.solicitor.writer.Writer; + +/** + * An {@link InventoryProcessor} might perform changes on the data model. {@link InventoryProcessor}s are called after + * reading the raw model data {@link Engagement}, {@link Application}s, {@link ApplicationComponent}s, + * {@link RawLicense}s with the {@link Reader}s and before doing the reporting via the {@link Writer}s. + * + * The {@link RuleEngine} is the most important {@link InventoryProcessor}. It executes the central Drools rule engine. + * + * Execution order of {@link InventoryProcessor}s is controlled by an {@link Order} annotation. + */ +public interface InventoryProcessor { + + /** + * Processing order: Execute before rule engine. Use in {@link Order} annotation of the implementing spring bean. + */ + public static final int BEFORE_RULE_ENGINE = 100; + + /** + * Processing order to be used for the rule engine. Use in {@link Order} annotation of the implementing spring bean. + */ + public static final int RULE_ENGINE = 200; + + /** + * Processing order: Execute after rule engine. Use in {@link Order} annotation of the implementing spring bean. + */ + public static final int AFTER_RULE_ENGINE = 300; + + /** + * Processes the data model. + * + * @param modelRoot the root object of the data model + */ + public void processInventory(ModelRoot modelRoot); + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java index 0ae58661..e84c904b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java @@ -26,7 +26,6 @@ import com.devonfw.tools.solicitor.model.masterdata.Application; import com.devonfw.tools.solicitor.reader.Reader; import com.devonfw.tools.solicitor.reader.ReaderFactory; -import com.devonfw.tools.solicitor.ruleengine.RuleEngine; import com.devonfw.tools.solicitor.writer.WriterFacade; /** @@ -52,7 +51,7 @@ public class Solicitor { private ReaderFactory readerFactory; @Autowired - private RuleEngine ruleEngine; + private InventoryProcessor[] inventoryProcessors; @Autowired private WriterFacade writerFacade; @@ -114,7 +113,7 @@ private void mainProcessing(CommandLineOptions clo) { modelRoot = this.modelImporterExporter.loadModel(clo.pathForLoad); } else { readInventory(); - this.ruleEngine.executeRules(modelRoot); + runInventoryProcessors(modelRoot); modelRoot.completeData(); } if (clo.save) { @@ -149,6 +148,19 @@ private void readInventory() { } } } + + } + + /** + * Execute the inventoryProcessors. + * + * @param modelRoot the root of the data model + */ + private void runInventoryProcessors(ModelRoot modelRoot) { + + for (InventoryProcessor inventoryProcessor : this.inventoryProcessors) { + inventoryProcessor.processInventory(modelRoot); + } } private void markApplicationMissingData(Application app) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LicenseTextHelper.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LicenseTextHelper.java index 42203cb4..782c2dfd 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LicenseTextHelper.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LicenseTextHelper.java @@ -1,5 +1,6 @@ package com.devonfw.tools.solicitor.common; +import org.apache.commons.lang.WordUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,4 +47,35 @@ public static String replaceLongHtmlContent(String rawContent) { } + /** + * Checks if the lines of the given text exceed the max allowed width. If yes the wrap the text to the given width. If + * not then return the original text. + * + * @param stringToBeWrapped the string that should be line wrapped + * @param maxAllowedWidth the maximum allowed line width without wrapping + * @param wrapWidth the new line length when doing wrapping + * @return the wrapped string + */ + public static String wrapIfNecessary(String stringToBeWrapped, int maxAllowedWidth, int wrapWidth) { + + if (stringToBeWrapped == null) { + return stringToBeWrapped; + } + + String[] lines = stringToBeWrapped.split("\r\n|\r|\n"); + boolean needsWrapping = false; + for (String line : lines) { + if (line.length() > maxAllowedWidth) { + needsWrapping = true; + } + } + if (!needsWrapping) { + return stringToBeWrapped; + } else { + for (int i = 0; i < lines.length; i++) { + lines[i] = WordUtils.wrap(lines[i], wrapWidth); + } + return String.join(System.lineSeparator(), lines); + } + } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 7a00f853..0269cde4 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -75,9 +75,15 @@ public enum LogMessages { "At least one license text contained a large amount of raw HTML and was substituted by placeholder text '{}'"), // MULTIPLE_DECISIONTABLES(51, "Multiple decision tables in both .xls and .csv format. Prioritizing '{}.xls'."), // ADDING_ADDITIONALWRITER_CONFIG(52, "Merging config: Adding additional writers to base config from {}"), // - NOT_A_VALID_NPM_PACKAGE_IDENTIFIER(53, - "{} is not a valid identifier for an NPM package"), NOT_A_VALID_NPM_PACKAGE_NAME(54, - "{} is not a valid name for an NPM package"); + NOT_A_VALID_NPM_PACKAGE_IDENTIFIER(53, "{} is not a valid identifier for an NPM package"), // + NOT_A_VALID_NPM_PACKAGE_NAME(54, "{} is not a valid name for an NPM package"), // + SCANCODE_PROCESSOR_STARTING(54, + "Experimental feature ACTIVE: Start enriching the inventory data with Scancode data (as far as available)"), // + SCANCODE_FEATURE_DEACTIVATED(55, + "The experimental feature for enriching the inventory with scancode data is DEACTIVATED"), // + SCANCODE_INFO_READ(56, "Scancode information was read for {} out of {} ApplicationComponents"), // + CURATIONS_NOT_EXISTING(57, "Curations file '{}' not found. No curations will be applied."), // + CURATIONS_PROCESSING(58, "Curations file '{}' exists. Applying curations."); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/ResourceToFileCopier.java b/core/src/main/java/com/devonfw/tools/solicitor/common/ResourceToFileCopier.java index fb46e11b..19ca1445 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/ResourceToFileCopier.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/ResourceToFileCopier.java @@ -222,8 +222,8 @@ public String copyReourcesToFile(ResourceGroup resourceGroup, String targetDir) "new_project/rules/LegalEvaluationProject.xls") .withCopyOperation("classpath:starters/rules/LegalPreEvaluationProject.xls", "new_project/rules/LegalPreEvaluationProject.xls") - .withCopyOperation("classpath:starters/rules/LicenseAssignmentProject.xls", - "new_project/rules/LicenseAssignmentProject.xls") + .withCopyOperation("classpath:starters/rules/LicenseAssignmentV2Project.xls", + "new_project/rules/LicenseAssignmentV2Project.xls") .withCopyOperation("classpath:starters/rules/LicenseNameMappingProject.xls", "new_project/rules/LicenseNameMappingProject.xls") .withCopyOperation("classpath:starters/rules/LicenseSelectionProject.xls", diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/AbstractSingleKindPackageURLHandler.java b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/AbstractSingleKindPackageURLHandler.java index fc501ed9..1674dd77 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/AbstractSingleKindPackageURLHandler.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/AbstractSingleKindPackageURLHandler.java @@ -30,7 +30,11 @@ public String pathFor(String packageUrl) { sb.append(purl.getName()).append("/"); sb.append(purl.getVersion()); - return sb.toString(); + String result = sb.toString(); + if (result.contains("..")) { + throw new IllegalArgumentException("A path constructed for a packageUrl must never contain '..' : " + result); + } + return result; } @Override diff --git a/core/src/main/java/com/devonfw/tools/solicitor/config/RuleConfig.java b/core/src/main/java/com/devonfw/tools/solicitor/config/RuleConfig.java index a1ef30e9..f5a6e268 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/config/RuleConfig.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/config/RuleConfig.java @@ -28,6 +28,12 @@ public class RuleConfig { @JsonProperty private String description; + @JsonProperty + private boolean deprecationWarnOnly; + + @JsonProperty + private String deprecationDetails; + /** * This method gets the field ruleGroup. * @@ -88,6 +94,26 @@ public boolean isOptional() { return this.optional; } + /** + * Gets deprecationWarnOnly. + * + * @return deprecationWarnOnly + */ + public boolean isDeprecationWarnOnly() { + + return this.deprecationWarnOnly; + } + + /** + * Gets deprecationDetails. + * + * @return deprecationDetails + */ + public String getDeprecationDetails() { + + return this.deprecationDetails; + } + /** * This method sets the field ruleGroup. * @@ -147,4 +173,23 @@ public void setOptional(boolean optional) { this.optional = optional; } + + /** + * Set deprecationWarnOnly. + * + * @param deprecationWarnOnly new value of deprecationWarnOnly. + */ + public void setDeprecationWarnOnly(boolean deprecationWarnOnly) { + + this.deprecationWarnOnly = deprecationWarnOnly; + } + + /** + * @param deprecationDetails new value of {@link #getDeprecationDetails}. + */ + public void setDeprecationDetails(String deprecationDetails) { + + this.deprecationDetails = deprecationDetails; + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java index 07605c96..5da088c5 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java @@ -117,6 +117,10 @@ private void readApplicationComponents(Application application, JsonNode applica if (readModelVersion >= LOWEST_VERSION_WITH_PACKAGE_URL) { packageUrl = applicationComponentNode.get("packageUrl").asText(null); } + JsonNode copyrightNode = applicationComponentNode.get("copyrights"); + String copyrights = copyrightNode != null ? copyrightNode.asText(null) : null; + JsonNode noticeFileUrlNode = applicationComponentNode.get("noticeFileUrl"); + String noticeFileUrl = noticeFileUrlNode != null ? noticeFileUrlNode.asText(null) : null; JsonNode normalizedLicensesNode = applicationComponentNode.get("normalizedLicenses"); JsonNode rawLicensesNode = applicationComponentNode.get("rawLicenses"); @@ -130,6 +134,8 @@ private void readApplicationComponents(Application application, JsonNode applica applicationComponent.setVersion(version); applicationComponent.setRepoType(repoType); applicationComponent.setPackageUrl(packageUrl); + applicationComponent.setCopyrights(copyrights); + applicationComponent.setNoticeFileUrl(noticeFileUrl); readNormalizedLicenses(applicationComponent, normalizedLicensesNode, readModelVersion); readRawLicenses(applicationComponent, rawLicensesNode, readModelVersion); @@ -267,6 +273,8 @@ private void readRawLicenses(ApplicationComponent applicationComponent, JsonNode String declaredLicense = rawLicenseNode.get("declaredLicense").asText(null); String licenseUrl = rawLicenseNode.get("licenseUrl").asText(null); String trace = rawLicenseNode.get("trace").asText(null); + JsonNode originNode = rawLicenseNode.get("origin"); + String origin = originNode != null ? originNode.asText(null) : null; boolean specialHandling = rawLicenseNode.get("specialHandling").asBoolean(); RawLicense rawLicense = this.modelFactory.newRawLicense(); @@ -274,6 +282,7 @@ private void readRawLicenses(ApplicationComponent applicationComponent, JsonNode rawLicense.setDeclaredLicense(declaredLicense); rawLicense.setLicenseUrl(licenseUrl); rawLicense.setTrace(trace); + rawLicense.setOrigin(origin); rawLicense.setSpecialHandling(specialHandling); } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java index 8328cb44..5210fd51 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java @@ -90,7 +90,10 @@ public Application newApplication(String name, String releaseId, String releaseD @Override public ApplicationComponent newApplicationComponent() { - return new ApplicationComponentImpl(); + ApplicationComponentImpl result = new ApplicationComponentImpl(); + result.setLicenseContentProvider(this.licenseContentProvider); + return result; + } /** {@inheritDoc} */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java index 8212feb3..37251053 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java @@ -8,7 +8,10 @@ import java.util.Collections; import java.util.List; +import com.devonfw.tools.solicitor.common.LicenseTextHelper; import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.common.content.ContentProvider; +import com.devonfw.tools.solicitor.common.content.web.WebContent; import com.devonfw.tools.solicitor.model.impl.AbstractModelObject; import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; import com.devonfw.tools.solicitor.model.inventory.NormalizedLicense; @@ -32,6 +35,8 @@ public class ApplicationComponentImpl extends AbstractModelObject implements App private String ossHomepage; + private String noticeFileUrl; + private String groupId; private String artifactId; @@ -42,10 +47,14 @@ public class ApplicationComponentImpl extends AbstractModelObject implements App private String packageUrl; + private String copyrights; + private List normalizedLicenses = new ArrayList<>(); private List rawLicenses = new ArrayList<>(); + private ContentProvider licenseContentProvider; + /** {@inheritDoc} */ @Override public void addNormalizedLicense(NormalizedLicense normalizedLicense) { @@ -60,6 +69,12 @@ public void addRawLicense(RawLicense rawLicense) { this.rawLicenses.add(rawLicense); } + @Override + public void removeAllRawLicenses() { + + this.rawLicenses = new ArrayList<>(); + } + /** {@inheritDoc} */ @Override protected Application doGetParent() { @@ -87,7 +102,8 @@ public String getArtifactId() { public String[] getDataElements() { return new String[] { this.groupId, this.artifactId, this.version, getRepoType(), getPackageUrl(), getOssHomepage(), - getUsagePattern().toString(), isOssModified() ? "true" : "false" }; + getNoticeFileUrl(), getNoticeFileContent(), getUsagePattern().toString(), isOssModified() ? "true" : "false", + getCopyrights() }; } /** {@inheritDoc} */ @@ -101,8 +117,8 @@ public String getGroupId() { @Override public String[] getHeadElements() { - return new String[] { "groupId", "artifactId", "version", "repoType", "packageUrl", "ossHomepage", "usagePattern", - "ossModified" }; + return new String[] { "groupId", "artifactId", "version", "repoType", "packageUrl", "ossHomepage", "noticeFileUrl", + "noticeFileContent", "usagePattern", "ossModified", "copyrights" }; } /** {@inheritDoc} */ @@ -119,6 +135,22 @@ public String getOssHomepage() { return this.ossHomepage; } + /** {@inheritDoc} */ + @Override + public String getNoticeFileUrl() { + + return this.noticeFileUrl; + } + + /** {@inheritDoc} */ + @Override + @JsonIgnore + public String getNoticeFileContent() { + + return LicenseTextHelper + .replaceLongHtmlContent(this.licenseContentProvider.getContentForUri(this.noticeFileUrl).getContent()); + } + /** {@inheritDoc} */ @Override public List getRawLicenses() { @@ -161,6 +193,17 @@ public boolean isOssModified() { return this.ossModified; } + /** + * This method gets the field licenseContentProvider. + * + * @return the field licenseContentProvider + */ + @JsonIgnore + public ContentProvider getLicenseContentProvider() { + + return this.licenseContentProvider; + } + /** {@inheritDoc} */ @Override public void setApplication(Application application) { @@ -193,6 +236,13 @@ public void setOssHomepage(String ossHomepage) { this.ossHomepage = ossHomepage; } + /** {@inheritDoc} */ + @Override + public void setNoticeFileUrl(String noticeFileUrl) { + + this.noticeFileUrl = noticeFileUrl; + } + /** {@inheritDoc} */ @Override public void setOssModified(boolean ossModified) { @@ -249,4 +299,28 @@ public void completeData() { } + @Override + public String getCopyrights() { + + return this.copyrights; + } + + @Override + public void setCopyrights(String copyrights) { + + this.copyrights = copyrights; + // TODO Auto-generated method stub + + } + + /** + * This method sets the field licenseContentProvider. + * + * @param licenseContentProvider the new value of the field licenseContentProvider + */ + public void setLicenseContentProvider(ContentProvider licenseContentProvider) { + + this.licenseContentProvider = licenseContentProvider; + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java index b892d376..5d39ab09 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java @@ -126,12 +126,12 @@ public String getCopyLeft() { public String[] getDataElements() { return new String[] { this.declaredLicense, this.licenseUrl, getDeclaredLicenseContent(), - this.normalizedLicenseType, this.normalizedLicense, this.normalizedLicenseUrl, this.effectiveNormalizedLicenseType, - this.effectiveNormalizedLicense, this.effectiveNormalizedLicenseUrl, getEffectiveNormalizedLicenseContent(), - this.legalPreApproved, this.copyLeft, this.licenseCompliance, this.licenseRefUrl, getLicenseRefContent(), - this.includeLicense, this.includeSource, this.reviewedForRelease, this.comments, this.legalApproved, - this.legalComments, this.trace, this.guessedLicenseUrl, this.guessedLicenseUrlAuditInfo, - getGuessedLicenseContent() }; + this.normalizedLicenseType, this.normalizedLicense, this.normalizedLicenseUrl, getNormalizedLicenseContent(), + this.effectiveNormalizedLicenseType, this.effectiveNormalizedLicense, this.effectiveNormalizedLicenseUrl, + getEffectiveNormalizedLicenseContent(), this.legalPreApproved, this.copyLeft, this.licenseCompliance, + this.licenseRefUrl, getLicenseRefContent(), this.includeLicense, this.includeSource, this.reviewedForRelease, + this.comments, this.legalApproved, this.legalComments, this.trace, this.guessedLicenseUrl, + this.guessedLicenseUrlAuditInfo, getGuessedLicenseContent() }; } /** {@inheritDoc} */ @@ -185,11 +185,11 @@ public String getEffectiveNormalizedLicenseUrl() { public String[] getHeadElements() { return new String[] { "declaredLicense", "licenseUrl", "declaredLicenseContent", "normalizedLicenseType", - "normalizedLicense", "normalizedLicenseUrl", "effectiveNormalizedLicenseType", "effectiveNormalizedLicense", - "effectiveNormalizedLicenseUrl", "effectiveNormalizedLicenseContent", "legalPreApproved", "copyLeft", - "licenseCompliance", "licenseRefUrl", "licenseRefContent", "includeLicense", "includeSource", "reviewedForRelease", - "comments", "legalApproved", "legalComments", "trace", "guessedLicenseUrl", "guessedLicenseUrlAuditInfo", - "guessedLicenseContent" }; + "normalizedLicense", "normalizedLicenseUrl", "normalizedLicenseContent", "effectiveNormalizedLicenseType", + "effectiveNormalizedLicense", "effectiveNormalizedLicenseUrl", "effectiveNormalizedLicenseContent", + "legalPreApproved", "copyLeft", "licenseCompliance", "licenseRefUrl", "licenseRefContent", "includeLicense", + "includeSource", "reviewedForRelease", "comments", "legalApproved", "legalComments", "trace", "guessedLicenseUrl", + "guessedLicenseUrlAuditInfo", "guessedLicenseContent" }; } /** {@inheritDoc} */ @@ -289,6 +289,15 @@ public String getNormalizedLicenseUrl() { return this.normalizedLicenseUrl; } + /** {@inheritDoc} */ + @Override + @JsonIgnore + public String getNormalizedLicenseContent() { + + return LicenseTextHelper + .replaceLongHtmlContent(this.licenseContentProvider.getContentForUri(this.normalizedLicenseUrl).getContent()); + } + /** {@inheritDoc} */ @Override public String getReviewedForRelease() { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java index 681b2bcc..77eb5ab1 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java @@ -20,6 +20,8 @@ public class RawLicenseImpl extends AbstractModelObject implements RawLicense { private String trace; + private String origin; + private boolean specialHandling; private ApplicationComponent applicationComponent; @@ -43,7 +45,7 @@ public ApplicationComponent getApplicationComponent() { @Override public String[] getDataElements() { - return new String[] { this.declaredLicense, this.licenseUrl, this.trace }; + return new String[] { this.declaredLicense, this.licenseUrl, this.trace, this.origin }; } /** {@inheritDoc} */ @@ -57,7 +59,7 @@ public String getDeclaredLicense() { @Override public String[] getHeadElements() { - return new String[] { "declaredLicense", "licenseUrl", "trace" }; + return new String[] { "declaredLicense", "licenseUrl", "trace", "origin" }; } /** {@inheritDoc} */ @@ -74,6 +76,13 @@ public String getTrace() { return this.trace; } + /** {@inheritDoc} */ + @Override + public String getOrigin() { + + return this.origin; + } + /** {@inheritDoc} */ @Override public boolean isSpecialHandling() { @@ -120,6 +129,13 @@ public void setTrace(String trace) { this.trace = trace; } + /** {@inheritDoc} */ + @Override + public void setOrigin(String origin) { + + this.origin = origin; + } + /** {@inheritDoc} */ @Override public void completeData() { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java index 48ebc735..5c016024 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java @@ -24,6 +24,11 @@ public interface ApplicationComponent { */ void addRawLicense(RawLicense rawLicense); + /** + * Removes all raw licenses. Might be used if resetting license information is required. + */ + void removeAllRawLicenses(); + /** * This method gets the field application. * @@ -59,6 +64,20 @@ public interface ApplicationComponent { */ String getOssHomepage(); + /** + * This method gets the field noticeFileUrl. + * + * @return the field noticeFileUrl + */ + String getNoticeFileUrl(); + + /** + * This method gets the field noticeFileContent. + * + * @return the field noticeFileContent + */ + String getNoticeFileContent(); + /** * This method gets an unmodifiable copy of the field rawLicenses. * @@ -104,6 +123,8 @@ public interface ApplicationComponent { */ boolean isOssModified(); + String getCopyrights(); + /** * Sets the {@link Application}. * @@ -132,6 +153,13 @@ public interface ApplicationComponent { */ void setOssHomepage(String ossHomepage); + /** + * This method sets the field noticeFileUrl. + * + * @param noticeFileUrl the new value of the field noticeFileUrl + */ + void setNoticeFileUrl(String noticeFileUrl); + /** * This method sets the field ossModified. * @@ -160,6 +188,13 @@ public interface ApplicationComponent { */ void setRepoType(String repoType); + /** + * This method sets the field copyrights. + * + * @param copyrights the new value of the field copyrights + */ + void setCopyrights(String copyrights); + /** * This method sets the field packageUrl. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java index 165ef7fe..73d53ec9 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java @@ -153,6 +153,13 @@ public interface NormalizedLicense { */ String getNormalizedLicenseUrl(); + /** + * Gets the content referenced by {@link #getNormalizedLicenseUrl()} + * + * @return the referenced content. + */ + String getNormalizedLicenseContent(); + /** * This method gets the field reviewedForRelease. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java index 78d5aaca..41c5322d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java @@ -33,6 +33,13 @@ public interface RawLicense { */ String getTrace(); + /** + * This method gets the field origin. + * + * @return the field origin + */ + String getOrigin(); + /** * This method gets the field specialHandling. * @@ -75,6 +82,13 @@ public interface RawLicense { */ void setTrace(String trace); + /** + * This method sets the field origin. + * + * @param origin the new value of the field origin + */ + void setOrigin(String origin); + /** * Complete the data of this object by setting members which are derived from other members. */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/AbstractReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/AbstractReader.java index f57195fd..7a7045fe 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/AbstractReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/AbstractReader.java @@ -59,10 +59,11 @@ public void doLogging(String sourceUrl, Application application, int readCompone */ public void addRawLicense(ApplicationComponent appComponent, String name, String url, String path) { - RawLicense mlic = modelFactory.newRawLicense(); + RawLicense mlic = this.modelFactory.newRawLicense(); mlic.setApplicationComponent(appComponent); mlic.setDeclaredLicense(name); mlic.setLicenseUrl(url); + mlic.setOrigin(this.getClass().getSimpleName().toLowerCase()); String trace; if (name == null && url == null) { trace = "+ Component info (without license) read in '" + getSupportedTypes() + "' format from '" + path + "'"; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java index 3eac7b7b..ca6b5461 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java @@ -76,7 +76,11 @@ public void readInventory(String type, String sourceUrl, Application application // check whether VendorUrl is included in input file or not if (attributes.size() == 6) { - homePage = attributes.get(4); + if (attributes.get(4) != null && !attributes.get(4).isBlank()) { + homePage = attributes.get(4); + } else { + homePage = repo; + } } else if (attributes.size() == 5) { homePage = repo; } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/RuleEngine.java b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/RuleEngine.java index dd97908d..56d1bbdf 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/RuleEngine.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/RuleEngine.java @@ -4,18 +4,11 @@ package com.devonfw.tools.solicitor.ruleengine; -import com.devonfw.tools.solicitor.model.ModelRoot; +import com.devonfw.tools.solicitor.InventoryProcessor; /** * Interface of a rule engine which works on the data of the Solicitor data model. */ -public interface RuleEngine { - - /** - * Executes all rules. - * - * @param modelRoot the {@link ModelRoot} which is the root objects of the data model to be processed. - */ - public void executeRules(ModelRoot modelRoot); +public interface RuleEngine extends InventoryProcessor { } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java index a34d8633..41778355 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java @@ -23,9 +23,12 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; +import com.devonfw.tools.solicitor.InventoryProcessor; import com.devonfw.tools.solicitor.SolicitorSetup; +import com.devonfw.tools.solicitor.common.DeprecationChecker; import com.devonfw.tools.solicitor.common.InputStreamFactory; import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.config.RuleConfig; @@ -43,6 +46,7 @@ * */ @Component +@Order(InventoryProcessor.RULE_ENGINE) public class DroolsRuleEngine implements RuleEngine { private static final Logger LOG = LoggerFactory.getLogger(DroolsRuleEngine.class); @@ -59,13 +63,16 @@ public class DroolsRuleEngine implements RuleEngine { @Autowired private SolicitorSetup setup; + @Autowired + private DeprecationChecker deprecationChecker; + /** * {@inheritDoc} * * Each set of rules given by a RuleConfig (e.g. a single decision table) will be executed in a separate Kie session. */ @Override - public void executeRules(ModelRoot modelRoot) { + public void processInventory(ModelRoot modelRoot) { int rulesFired = 0; for (RuleConfig rc : this.setup.getRuleSetups()) { @@ -93,6 +100,11 @@ private int executeRuleGroup(ModelRoot modelRoot, RuleConfig rc) { return 0; } + // check if this rule group was marked as deprecated + if (rc.getDeprecationDetails() != null && !rc.getDeprecationDetails().isBlank()) { + this.deprecationChecker.check(rc.isDeprecationWarnOnly(), rc.getDeprecationDetails()); + } + KieSession ksession = prepareSession(rc); insertFacts(ksession, modelRoot); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java new file mode 100644 index 00000000..7c6a0f89 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java @@ -0,0 +1,227 @@ +package com.devonfw.tools.solicitor.scancode; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * Data structure for holding the scan information from scancode for a single package. + */ +public class ComponentScancodeInfos { + + /** + * Holds the info about a single found license. + */ + public class LicenseInfo { + + /** + * Id for of the license. + */ + public String id; + + /** + * SPDX-ID of the license. + */ + public String spdxid; + + /** + * The score of the license. + */ + public double licenseScore; + + /** + * Path to the license file. + */ + public String licenseFilePath; + + /** + * The score of the license file. + */ + public double licenseFileScore; + + /** + * The constructor. + * + * @param id the id + * @param spdxid the spdx id + * @param defaultUrl the default URL of the license text + * @param licenseScore the score for the license + * @param licenseFilePath the path to the license file + * @param licenseFileScore the score of the license file + */ + public LicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath, + double licenseFileScore) { + + super(); + this.id = id; + this.spdxid = spdxid; + this.licenseScore = licenseScore; + if (licenseFileScore >= ComponentScancodeInfos.this.minLicensefilePercentage) { + this.licenseFilePath = licenseFilePath; + this.licenseFileScore = licenseFileScore; + } else { + this.licenseFilePath = defaultUrl; + this.licenseFileScore = 0.0; + } + } + } + + private static final double MIN_NOTICEFILE_PERCENTAGE = 0.0; + + private SortedSet copyrights = new TreeSet<>(); + + private SortedMap licenses = new TreeMap<>(); + + private double noticeFileScore = 0; + + private String noticeFilePath = null; + + private String url; + + private double minLicensefilePercentage; + + private double minLicenseScore; + + /** + * The constructor. + * + * @param minLicenseScore the minimum score to take license findings into account + * @param minLicensefilePercentage the minimum percentage of license text to possibly use file as license file + */ + public ComponentScancodeInfos(double minLicenseScore, double minLicensefilePercentage) { + + super(); + this.minLicenseScore = minLicenseScore; + this.minLicensefilePercentage = minLicensefilePercentage; + } + + /** + * Adds a single copyright line. + * + * @param copyright the copyright + */ + public void addCopyright(String copyright) { + + this.copyrights.add(copyright); + } + + /** + * Gets all copyrights. + * + * @return the copyrights + */ + public Collection getCopyrights() { + + return Collections.unmodifiableCollection(this.copyrights); + } + + /** + * Clears all found copyrights. + */ + public void clearCopyrights() { + + this.copyrights = new TreeSet<>(); + } + + /** + * Adds a license or updates the information if the relevant scores exceed the required thresholds and the score is + * better than the score of already existing information. + * + * @param licenseId the license id + * @param licenseName the name of the license (SPDS-ID) + * @param licenseDefaultUrl the url of the generic license text + * @param score the score of the license finding + * @param filePath path to the license file + * @param fileScore score of the license file + */ + public void addLicense(String licenseId, String licenseName, String licenseDefaultUrl, double score, String filePath, + double fileScore) { + + if (this.licenses.containsKey(licenseId)) { + LicenseInfo existingLicenseInfo = this.licenses.get(licenseId); + + double resultingScore = Math.max(existingLicenseInfo.licenseScore, score); + String resultingFilePath = existingLicenseInfo.licenseFilePath; + double resultingFileScore = existingLicenseInfo.licenseFileScore; + if (fileScore > existingLicenseInfo.licenseFileScore) { + resultingFilePath = filePath; + resultingFileScore = fileScore; + } + this.licenses.put(licenseId, new LicenseInfo(licenseId, licenseName, licenseDefaultUrl, resultingScore, + resultingFilePath, resultingFileScore)); + + } else { + if (score >= this.minLicenseScore) { + this.licenses.put(licenseId, + new LicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, filePath, fileScore)); + } + } + } + + /** + * Gets all licenses. + * + * @return all licenses + */ + public Map getLicenses() { + + return Collections.unmodifiableSortedMap(this.licenses); + } + + /** + * Clears all stored licenses. + */ + public void clearLicenses() { + + this.licenses = new TreeMap<>(); + } + + /** + * Stores the path to a NOTICE file if the score exceeds the minimum required score and is higher than the score of + * already existing information. + * + * @param path path to the NOTICE file + * @param score score of the file + */ + public void addNoticeFilePath(String path, double score) { + + if (score > this.noticeFileScore && score >= MIN_NOTICEFILE_PERCENTAGE) { + this.noticeFilePath = path; + this.noticeFileScore = score; + } + } + + /** + * Gets the path to the notice file (if any) + * + * @return path to the notice file + */ + public String getNoticeFilePath() { + + return this.noticeFilePath; + } + + /** + * Gets the url to the license text + * + * @return url to the license text + */ + public String getUrl() { + + return this.url; + } + + /** + * Sets the url to the license text + * + * @param url new value of {@link #getUrl()}. + */ + public void setUrl(String url) { + + this.url = url; + } +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java new file mode 100644 index 00000000..997b9926 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java @@ -0,0 +1,21 @@ +package com.devonfw.tools.solicitor.scancode; + +/** + * Adapter for reading Scancode information for a package, applying any given curations and returning the information as + * a {@link ComponentScancodeInfos} object. + */ +public interface ScancodeAdapter { + + /** + * Retrieves the Scancode information and curations for a package identified by the given package URL. Returns the + * data as a {@link ComponentScancodeInfos} object. + * + * @param packageUrl The identifier of the package for which information is requested + * @return the data derived from the scancode results after applying any defined curations. null is + * returned if no data is available, + * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available + * no exception will be thrown. Instead null will be return in such a case. + */ + ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throws ScancodeException; + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java new file mode 100644 index 00000000..9b9eb761 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java @@ -0,0 +1,36 @@ +package com.devonfw.tools.solicitor.scancode; + +/** + * Exception indicating that the Scancode information could not be read. + */ +public class ScancodeException extends Exception { + + /** + * A constructor. + */ + public ScancodeException() { + + } + + /** + * A constructor. + * + * @param message the message of the exception + */ + public ScancodeException(String message) { + + super(message); + } + + /** + * A constructor. + * + * @param message the message of the exception + * @param cause the underlying {@link Throwable}. + */ + public ScancodeException(String message, Throwable cause) { + + super(message, cause); + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java new file mode 100644 index 00000000..791948b5 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java @@ -0,0 +1,273 @@ +package com.devonfw.tools.solicitor.scancode; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +/** + * Adapter for reading Scancode information for a package from a file, applying any given curations and returning the + * information as a {@link ComponentScancodeInfos} object. + */ +@Component +public class ScancodeFileAdapter implements ScancodeAdapter { + + private static final Logger LOG = LoggerFactory.getLogger(ScancodeFileAdapter.class); + + private static final ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + + private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + + private String repoBasePath; + + private String curationsFileName; + + private double minLicenseScore; + + private double minLicensefilePercentage; + + private boolean curationsExistenceLogged; + + @Autowired + private AllKindsPackageURLHandler packageURLHandler; + + /** + * The constructor. + */ + + public ScancodeFileAdapter() { + + } + + /** + * Sets repoBasePath. + * + * @param repoBasePath new value of repoBasePath. + */ + @Value("${solicitor.scancode.repo-base-path}") + public void setRepoBasePath(String repoBasePath) { + + this.repoBasePath = repoBasePath; + } + + /** + * Sets curationsFileName. + * + * @param curationsFileName new value of curationsFileName. + */ + @Value("${solicitor.scancode.curations-filename}") + public void setCurationsFileName(String curationsFileName) { + + this.curationsFileName = curationsFileName; + } + + /** + * Sets minLicenseScore. + * + * @param minLicenseScore new value of minLicenseScore. + */ + @Value("${solicitor.scancode.min-license-score}") + public void setMinLicenseScore(double minLicenseScore) { + + this.minLicenseScore = minLicenseScore; + } + + /** + * Sets minLicensefilePercentage. + * + * @param minLicensefilePercentage new value of minLicensefilePercentage. + */ + @Value("${solicitor.scancode.min-licensefile-percentage}") + public void setMinLicensefilePercentage(double minLicensefilePercentage) { + + this.minLicensefilePercentage = minLicensefilePercentage; + } + + /** + * Retrieves the Scancode information and curations for a package identified by the given package URL. Returns the + * data as a {@link ComponentScancodeInfos} object. + * + * @param packageUrl The identifier of the package for which information is requested + * @return the data derived from the scancode results after applying any defined curations. null is + * returned if no data is available, + * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available + * no exception will be thrown. Instead null will be return in such a case. + */ + @Override + public ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throws ScancodeException { + + ComponentScancodeInfos componentScancodeInfos = determineScancodeInformation(packageUrl); + if (componentScancodeInfos == null) { + return null; + } + applyCurations(packageUrl, componentScancodeInfos); + + return componentScancodeInfos; + + } + + /** + * Read scancode information for the given package from local file storage. + * + * @param packageUrl The package url of the package + * @return the read scancode information, null if no information was found + * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available + * no exception will be thrown. Instead null will be return in such a case. + */ + private ComponentScancodeInfos determineScancodeInformation(String packageUrl) throws ScancodeException { + + ComponentScancodeInfos componentScancodeInfos = new ComponentScancodeInfos(this.minLicenseScore, + this.minLicensefilePercentage); + String packagePathPart = this.packageURLHandler.pathFor(packageUrl); + String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json"; + + File scanCodeFile = new File(path); + if (!scanCodeFile.exists()) { + LOG.debug("No Scancode info available for PackageURL '{}'", packageUrl); + return null; + } + LOG.debug("Found Scancode info for PackageURL '{}'", packageUrl); + try (InputStream is = new FileInputStream(scanCodeFile)) { + + JsonNode scancodeJson = mapper.readTree(is); + + for (JsonNode file : scancodeJson.get("files")) { + if ("directory".equals(file.get("type").asText())) { + continue; + } + if (file.get("path").asText().contains("/NOTICE")) { + componentScancodeInfos.addNoticeFilePath("file:" + this.repoBasePath + "/" + + this.packageURLHandler.pathFor(packageUrl) + "/" + file.get("path").asText(), 100.0); + } + for (JsonNode cr : file.get("copyrights")) { + componentScancodeInfos.addCopyright(cr.get("value").asText()); + } + + for (JsonNode li : file.get("licenses")) { + String licenseid = li.get("key").asText(); + String licenseName = li.get("spdx_license_key").asText(); + String licenseDefaultUrl = li.get("scancode_text_url").asText(); + licenseDefaultUrl = normalizeLicenseUrl(packageUrl, licenseDefaultUrl); + double score = li.get("score").asDouble(); + String licenseFilePath = file.get("path").asText(); + licenseFilePath = normalizeLicenseUrl(packageUrl, licenseFilePath); + + componentScancodeInfos.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseFilePath, + file.get("percentage_of_license_text").asDouble()); + } + } + LOG.debug("Scancode info for package {}: {} license, {} copyrights, {} NOTICE files", packageUrl, + componentScancodeInfos.getLicenses().size(), componentScancodeInfos.getCopyrights().size(), + componentScancodeInfos.getNoticeFilePath() != null ? 1 : 0); + + } catch (IOException e) { + throw new ScancodeException("Could not read Scancode JSON", e); + } + return componentScancodeInfos; + } + + /** + * Checks for the existence of curations for the given package in the local file system and applies them to the + * component scancode infos. + * + * @param packageUrl the identifier of the package + * @param componentScancodeInfos the componentScancodeInfos to curate + * @throws ScancodeException if the curations could not be read + */ + private void applyCurations(String packageUrl, ComponentScancodeInfos componentScancodeInfos) + throws ScancodeException { + + String packagePathPart = this.packageURLHandler.pathFor(packageUrl); + + File curationsFile = new File(this.curationsFileName); + if (!curationsFile.exists()) { + if (!this.curationsExistenceLogged) { + // log only once + this.curationsExistenceLogged = true; + LOG.info(LogMessages.CURATIONS_NOT_EXISTING.msg(), this.curationsFileName); + } + } else { + if (!this.curationsExistenceLogged) { + // log only once + this.curationsExistenceLogged = true; + LOG.info(LogMessages.CURATIONS_PROCESSING.msg(), this.curationsFileName); + } + try (InputStream isc = new FileInputStream(this.curationsFileName)) { + + JsonNode curationsObj = yamlMapper.readTree(isc); + + for (JsonNode curations : curationsObj.get("artifacts")) { + String component = curations.get("name").asText(); + if (component.equals(packagePathPart)) { + ComponentScancodeInfos oneComponent = componentScancodeInfos; + if (curations.get("copyrights") != null) { + oneComponent.clearCopyrights(); + int authorCount = curations.get("copyrights").size(); + for (int i = 0; i < authorCount; i++) { + oneComponent.addCopyright(curations.get("copyrights").get(i).asText()); + } + } + if (curations.get("url") != null) { + String url = curations.get("url").asText(); + oneComponent.setUrl(url); + } + if (curations.get("licenses") != null) { + oneComponent.clearLicenses(); + for (JsonNode licenseNode : curations.get("licenses")) { + String license = licenseNode.get("license").asText(); + String url = licenseNode.get("url").asText(); + oneComponent.addLicense(license, license, "", 110, url, 110); + } + } + } + } + + } catch (IOException e) { + throw new ScancodeException("Could not read Curations JSON", e); + } + + } + + } + + /** + * Adjustment of license paths/urls so that they might retrieved + * + * @param packageUrl package url of the package + * @param licenseFilePath the original path or URL + * @return the adjustes patjh or url as a url + */ + private String normalizeLicenseUrl(String packageUrl, String licenseFilePath) { + + String adjustedLicenseFilePath; + if (licenseFilePath != null) { + if (licenseFilePath.startsWith("http")) { + // TODO + adjustedLicenseFilePath = licenseFilePath.replace("github.com", "raw.github.com").replace("/tree", ""); + } else { + adjustedLicenseFilePath = "file:" + this.repoBasePath + "/" + this.packageURLHandler.pathFor(packageUrl) + "/" + + licenseFilePath; + LOG.debug("LOCAL LICENSE: " + licenseFilePath); + } + } else { + adjustedLicenseFilePath = null; + // licenseFilePath = null;// "??????";// defaultGithubLicenseURL(repo); + LOG.debug("NONLOCAL LICENSE: {} (was: {})" + adjustedLicenseFilePath, licenseFilePath); + } + return adjustedLicenseFilePath; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java new file mode 100644 index 00000000..9f2edece --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java @@ -0,0 +1,193 @@ +package com.devonfw.tools.solicitor.scancode; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.InventoryProcessor; +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.model.ModelFactory; +import com.devonfw.tools.solicitor.model.ModelRoot; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.inventory.RawLicense; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.scancode.ComponentScancodeInfos.LicenseInfo; + +/** + * An {@link InventoryProcessor} which looks up license information for the found application components / packages in + * the scancode file store. If license information is found then the license, copyright and notice file information of + * the model will be replaced by the data obtained from scancode. + * + */ +@Component +@Order(InventoryProcessor.BEFORE_RULE_ENGINE) +public class ScancodeInventoryProcessor implements InventoryProcessor { + + private static class Statistics { + public int componentsTotal; + + public int componentsWithScancodeInfos; + + public void add(Statistics statistics) { + + this.componentsTotal += statistics.componentsTotal; + this.componentsWithScancodeInfos += statistics.componentsWithScancodeInfos; + } + } + + /** + * Origin data for raw license objects created by this Class. + */ + private static final String ORIGIN_SCANCODE = "scancode"; + + private static final Logger LOG = LoggerFactory.getLogger(ScancodeInventoryProcessor.class); + + private boolean featureFlag = false; + + private ScancodeAdapter scancodeAdapter; + + private ModelFactory modelFactory; + + /** + * Sets the feature flag for activating/deactivating this feature. + * + * @param featureFlag the flag + */ + @Value("${solicitor.feature-flag.scancode}") + public void setFeatureFlag(boolean featureFlag) { + + this.featureFlag = featureFlag; + } + + /** + * The constructor. + */ + public ScancodeInventoryProcessor() { + + } + + @Override + public void processInventory(ModelRoot modelRoot) { + + if (!this.featureFlag) { + LOG.info(LogMessages.SCANCODE_FEATURE_DEACTIVATED.msg()); + } else { + LOG.warn(LogMessages.SCANCODE_PROCESSOR_STARTING.msg()); + + Statistics overall = new Statistics(); + for (Application application : modelRoot.getEngagement().getApplications()) { + for (ApplicationComponent ac : application.getApplicationComponents()) { + Statistics single = processApplicationComponent(ac); + overall.add(single); + } + } + LOG.info(LogMessages.SCANCODE_INFO_READ.msg(), overall.componentsWithScancodeInfos, overall.componentsTotal); + } + } + + /** + * Process a single {@link ApplicationComponent}. + * + * @param ac application component + * @return processing statistics + */ + private Statistics processApplicationComponent(ApplicationComponent ac) { + + Statistics statistics = new Statistics(); + statistics.componentsTotal = 1; + + if (ac.getPackageUrl() != null) { + ComponentScancodeInfos componentScancodeInfos; + try { + componentScancodeInfos = this.scancodeAdapter.getComponentScancodeInfos(ac.getPackageUrl()); + } catch (ScancodeException e) { + throw new SolicitorRuntimeException("Exception when reading scancode file", e); + } + if (componentScancodeInfos != null) { + statistics.componentsWithScancodeInfos = 1; + ac.removeAllRawLicenses(); + + if (componentScancodeInfos.getNoticeFilePath() != null) { + ac.setNoticeFileUrl(componentScancodeInfos.getNoticeFilePath()); + } + + for (LicenseInfo li : componentScancodeInfos.getLicenses().values()) { + addRawLicense(ac, li.spdxid, li.licenseFilePath, ORIGIN_SCANCODE); + } + String copyrights = String.join("\n", componentScancodeInfos.getCopyrights()); + ac.setCopyrights(copyrights); + // check whether VendorUrl is included in input file or not + if (componentScancodeInfos.getUrl() != null) { + ac.setOssHomepage(componentScancodeInfos.getUrl()); + } + } else { + // no scancode info found for ac + } + } else { + // can this happen? + } + return statistics; + } + + /** + * Performs logging. + * + * @param sourceUrl the URL from where the inventory data was read + * @param application the application + * @param readComponents number of read ApplicationComponents + * @param readLicenses number of read RawLicenses + */ + public void doLogging(String sourceUrl, Application application, int readComponents, int readLicenses) { + + LOG.info(LogMessages.READING_INVENTORY.msg(), readComponents, readLicenses, application.getName(), sourceUrl); + } + + /** + * Adds a {@link com.devonfw.tools.solicitor.model.inventory.RawLicense} to the given + * {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent}. + * + * @param appComponent a {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent} object. + * @param name a {@link java.lang.String} object. + * @param url a {@link java.lang.String} object. + * @param origin a {@link java.lang.String} object. + */ + public void addRawLicense(ApplicationComponent appComponent, String name, String url, String origin) { + + RawLicense mlic = this.modelFactory.newRawLicense(); + mlic.setApplicationComponent(appComponent); + mlic.setDeclaredLicense(name); + mlic.setLicenseUrl(url); + mlic.setOrigin(origin); + String trace; + trace = "+ Component/License info read from scancode information"; + + mlic.setTrace(trace); + } + + /** + * This method sets the field modelFactory. + * + * @param modelFactory the new value of the field modelFactory + */ + @Autowired + public void setModelFactory(ModelFactory modelFactory) { + + this.modelFactory = modelFactory; + } + + /** + * This method sets the field scancodeAdapter + * + * @param scancodeAdapter new value of scancodeAdapter. + */ + @Autowired + public void setScancodeAdapter(ScancodeAdapter scancodeAdapter) { + + this.scancodeAdapter = scancodeAdapter; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/EscapeTool.java b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/EscapeTool.java new file mode 100644 index 00000000..bd998014 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/EscapeTool.java @@ -0,0 +1,30 @@ +package com.devonfw.tools.solicitor.writer.velocity; + +import org.apache.commons.codec.digest.DigestUtils; + +import com.devonfw.tools.solicitor.common.LicenseTextHelper; + +/** + * TODO ohecker This type ... + * + */ +public class EscapeTool extends org.apache.velocity.tools.generic.EscapeTool { + + public String newLinesAsBreaks(String input) { + + return input != null ? input.replace("\n", "
") : null; + } + + public String wrap100to80(String input) { + + return LicenseTextHelper.wrapIfNecessary(input, 100, 80); + } + + public String hash(String input) { + + if (input == null) { + return ""; + } + return new DigestUtils("SHA-256").digestAsHex(input); + } +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java index 4189d92c..0669e6b3 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java @@ -14,7 +14,6 @@ import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; -import org.apache.velocity.tools.generic.EscapeTool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index a399ea39..874da74d 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -41,6 +41,20 @@ solicitor.classpath-guessedlicenseurl-cache-locations=licenseurls # Deprecated features are deactivated by default. If set to true they might be (temporarily) activated. solicitor.deprecated-features-allowed=false +## Feature flags for activation of non-standard/experimental functionality +# Incorporate scancode infos into model +solicitor.feature-flag.scancode=false + +## Parameters for controlling the processing of scancode information +# minimum score of detected license findings to be taken into account for Solicitor processing +solicitor.scancode.min-license-score=80.0 +# minimum "percentage_of_license_text" for a file to be possibly taken as license text +solicitor.scancode.min-licensefile-percentage=50.0 +# filename of the curationy.yaml file +solicitor.scancode.curations-filename=output/curations.yaml +# base path of the file repo where sources and scancode information is stored +solicitor.scancode.repo-base-path=output/Source + # If there is an exception while reading the raw inventory data for an application the processing will be aborted by default. # By setting this property to true processing will continue in case that the input file is not found. This allows processing # in multi application projects where some input files are not yet available. A warning (SOLI-045) will be logged in this case and diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg index 0d00e8d5..549ec9a2 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg +++ b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg @@ -26,13 +26,22 @@ "ruleSource" : "file:${cfgdir}/rules/LicenseAssignmentProject", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt", "ruleGroup" : "LicenseAssignmentProject", + "description" : "setting license in case that no one was detected or overwriting a wrongly detected license (project overrides; 'old' rule structure)", + "deprecationWarnOnly" : true, + "deprecationDetails" : "Use of decision table LicenseAssignmentProject is deprecated, use LicenseAssignmentV2Project instead. See release notes of release 1.4.0 for migration hints" + },{ + "type" : "dt", + "optional" : true, + "ruleSource" : "file:${cfgdir}/rules/LicenseAssignmentV2Project", + "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt", + "ruleGroup" : "LicenseAssignmentV2Project", "description" : "setting license in case that no one was detected or overwriting a wrongly detected license (project overrides)" },{ "type" : "dt", "optional" : false, - "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseAssignmentSample", - "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt", - "ruleGroup" : "LicenseAssignmentDefaults", + "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample", + "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt", + "ruleGroup" : "LicenseAssignmentV2Defaults", "description" : "setting license in case that no one was detected or overwriting a wrongly detected license" },{ "type" : "dt", @@ -150,6 +159,19 @@ "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents_guessedlicenses.sql", "UNIQUELICENSES" : "classpath:com/devonfw/tools/solicitor/sql/uniqueguessedlicenses.sql" } + },{ + "type" : "velo", + "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Attributions.vm", + "target" : "${cfgdir}/output/Attributions_${project}.html", + "description" : "A document containing the license information and attributions required by the OSS licenses", + "dataTables" : { + "ENGAGEMENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_engagements.sql", + "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql", + "UNIQUELICENSES" : "classpath:com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql", + "NOTICEFILES" : "classpath:com/devonfw/tools/solicitor/sql/noticefiles.sql", + "NONCOMMERCIALCOMPONENTS" : "classpath:com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql", + "NONCOMMERCIALCOMPONENTS_LICENSES" : "classpath:com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql" + } },{ "type" : "velo", "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Solicitor_Diff_Template_Sample.vm", diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls similarity index 81% rename from core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentSample.xls rename to core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls index bf73870a55b13eb4cfda501a21e30b20777b8c8d..dfdc7ad23235e7226fd0885bc3267ab877b038a5 100644 GIT binary patch delta 6439 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 diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt new file mode 100644 index 00000000..5fd79b30 --- /dev/null +++ b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 + +template header +ruleId +clientName +engagementName +applicationName +groupId +artifactId +version +origin +declaredLicense +url +normalizedLicenseType +normalizedLicense +normalizedLicenseUrl +comment + + +package com.devonfw.tools.solicitor.rules; + +import com.devonfw.tools.solicitor.model.inventory.NormalizedLicense; +import com.devonfw.tools.solicitor.model.inventory.RawLicense; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.Engagement; +import com.devonfw.tools.solicitor.ruleengine.drools.AuditEntryBuilder; +import com.devonfw.tools.solicitor.ruleengine.drools.ModelHelper; + + +template "License Assignment" + +rule "License Assignment @{ruleId} - mark RawLicense as done by specialHandling" salience -10000 + when + // make sure that the main conditions given here are the same as in the below rule + // this rule fires at the very end (salience -10000) of the group and + // assures that all records which have been processed by the other rule get + // marked with "specialHandling=true" so they wont be handled again + $e : Engagement( + clientName == "@{clientName}", + engagementName == "@{engagementName}", + $name : engagementName ) + $a : Application( + name == "@{applicationName}", + engagement == $e ) + $ac : ApplicationComponent( + ModelHelper.match( groupId, "@{groupId}" ), + ModelHelper.match( artifactId, "@{artifactId}" ), + ModelHelper.match( version, "@{version}" ), + application == $a ) + $rl : RawLicense( + specialHandling == false, + ModelHelper.match( origin, "@{origin}" ), + ModelHelper.match( declaredLicense, "@{declaredLicense}" ), + $licUrl : licenseUrl, + ModelHelper.match( licenseUrl, "@{url}" ), + applicationComponent == $ac ) + then + $rl.setSpecialHandling(true); + update($rl); + +end + +rule "License Assignment @{ruleId} - add given NormalizedLicense" salience -@{row.rowNumber} + when + $e : Engagement( + clientName == "@{clientName}", + engagementName == "@{engagementName}", + $name : engagementName ) + $a : Application( + name == "@{applicationName}", + engagement == $e ) + $ac : ApplicationComponent( + ModelHelper.match( groupId, "@{groupId}" ), + ModelHelper.match( artifactId, "@{artifactId}" ), + ModelHelper.match( version, "@{version}" ), + application == $a ) + $rl : RawLicense( + specialHandling == false, + ModelHelper.match( origin, "@{origin}" ), + ModelHelper.match( declaredLicense, "@{declaredLicense}" ), + $licUrl : licenseUrl, + ModelHelper.match( licenseUrl, "@{url}" ), + applicationComponent == $ac ) + not( NormalizedLicense(applicationComponent == $ac, normalizedLicense == "@{normalizedLicense}") ) // differs from above + + then + NormalizedLicense normalizedLicense = ModelHelper.newNormalizedLicense($rl ); + normalizedLicense.setNormalizedLicenseType("@{normalizedLicenseType}"); + normalizedLicense.setNormalizedLicense("@{normalizedLicense}"); + normalizedLicense.setNormalizedLicenseUrl($licUrl); + normalizedLicense.setNormalizedLicenseUrl( "@{normalizedLicenseUrl}" ); + ModelHelper.addCommentToNormalizedLicense(normalizedLicense, "@{comment}" ); + + // Create trace entry + AuditEntryBuilder aeb = AuditEntryBuilder.instance(); + aeb.withRuleId("@{ruleId}") + .withMatching("clientName", "@{clientName}" ) + .withMatching("engagementName", "@{engagementName}" ) + .withMatching("name", "@{applicationName}" ) + .withMatching("groupId", "@{groupId}" ) + .withMatching("artifactId", "@{artifactId}" ) + .withMatching("version", "@{version}" ) + .withMatching("declaredLicense", "@{declaredLicense}" ) + .withMatching("licenseUrl", "@{url}" ) + .withSetting("normalizedLicenseType", "@{normalizedLicenseType}" ) + .withSetting("normalizedLicense", "@{normalizedLicense}" ) + .withSetting("normalizedLicenseUrl", $licUrl, "taking url from read data" ) + .withSetting("normalizedLicenseUrl", "@{normalizedLicenseUrl}" ) + .withSetting("comment", "@{comment}") + .nop(); + ModelHelper.appendTraceToNormalizedLicense(normalizedLicense,aeb.build()); + + insert(normalizedLicense); + +end + +end template diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql new file mode 100644 index 00000000..342c69a7 --- /dev/null +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql @@ -0,0 +1,26 @@ +-- SPDX-License-Identifier: Apache-2.0 +-- +select + GROUP_CONCAT(DISTINCT a."applicationName" ORDER BY "applicationName" DESC SEPARATOR ', ') as APPS, + GROUP_CONCAT(DISTINCT ac."version" ORDER BY "version" DESC SEPARATOR ', ') as "version" , + ac."groupId", + ac."artifactId", + ac."ossHomepage", + ac."copyrights" +from + APPLICATION a, + APPLICATIONCOMPONENT ac, + NORMALIZEDLICENSE l +where + a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND + ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND + l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE') +group by + "groupId", + "artifactId", + "ossHomepage", + "copyrights" +order by + "groupId", + "artifactId", + "version" diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql new file mode 100644 index 00000000..d020df5d --- /dev/null +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql @@ -0,0 +1,31 @@ +-- SPDX-License-Identifier: Apache-2.0 +-- +select + GROUP_CONCAT(DISTINCT a."applicationName" ORDER BY "applicationName" DESC SEPARATOR ', ') as APPS, + GROUP_CONCAT(DISTINCT ac."version" ORDER BY "version" DESC SEPARATOR ', ') as "version" , + ac."groupId", + ac."artifactId", + ac."ossHomepage", + ac."copyrights", + l."normalizedLicense", + l."effectiveNormalizedLicense" +from + APPLICATION a, + APPLICATIONCOMPONENT ac, + NORMALIZEDLICENSE l +where + a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND + ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND + l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE') +group by + "groupId", + "artifactId", + "ossHomepage", + "copyrights", + "normalizedLicense", + "effectiveNormalizedLicense" +order by + "groupId", + "artifactId", + "version", + "normalizedLicense" diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql new file mode 100644 index 00000000..83e09d89 --- /dev/null +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql @@ -0,0 +1,20 @@ +-- SPDX-License-Identifier: Apache-2.0 +-- +-- returns all distinct notice file texts +select distinct + GROUP_CONCAT(DISTINCT CONCAT( ac."artifactId", ' (', ac."version", ')' ) ORDER BY "artifactId" ASC, "version" ASC SEPARATOR ', ') as "artifact", + ARRAY_AGG(DISTINCT ac."noticeFileContent" ORDER BY "noticeFileContent" DESC)[1] as "noticeFileContent", + UCASE(REGEXP_REPLACE(ac."noticeFileContent",'\s','')) as "unifiedNoticeFileContent" +from + APPLICATION a, + APPLICATIONCOMPONENT ac, + NORMALIZEDLICENSE l +where + a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND + ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND + ac."noticeFileUrl" is not null AND + l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE') +group by + "unifiedNoticeFileContent" +order by + "unifiedNoticeFileContent" diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql index c9cd8a8e..b9ff9f89 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql @@ -6,6 +6,7 @@ select ac."groupId", ac."artifactId", ac."packageUrl", + ac."copyrights", l."effectiveNormalizedLicense", l."effectiveNormalizedLicenseUrl", l."effectiveNormalizedLicenseContent", @@ -20,8 +21,9 @@ where l."effectiveNormalizedLicenseType" like 'OSS-%' group by "groupId", - "artifactId", + "artifactId", "packageUrl", + "copyrights", "effectiveNormalizedLicense", "effectiveNormalizedLicenseUrl", "effectiveNormalizedLicenseContent", diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossden_normalizedlicenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossden_normalizedlicenses.sql new file mode 100644 index 00000000..735e5406 --- /dev/null +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossden_normalizedlicenses.sql @@ -0,0 +1,29 @@ +-- SPDX-License-Identifier: Apache-2.0 +-- +-- generate all NormalizedLicenses in denormalized form including all hierachical data (allden -> "all denormalized") +select + CONCAT(NVL(a."applicationName",'-'),NVL(ac."groupId",'-'),NVL(ac."artifactId",'-'),NVL(ac."version",'-'),NVL(l."normalizedLicense",'-')) as CORR_KEY_0, + CONCAT(NVL(a."applicationName",'-'),NVL(ac."groupId",'-'),NVL(ac."artifactId",'-'),NVL(l."normalizedLicense",'-')) as CORR_KEY_1, + CONCAT(NVL(a."applicationName",'-'),NVL(ac."groupId",'-'),NVL(ac."artifactId",'-'),NVL(ac."version",'-')) as CORR_KEY_2, + CONCAT(NVL(a."applicationName",'-'),NVL(ac."groupId",'-'),NVL(ac."artifactId",'-')) as CORR_KEY_3, + e.*, + a.*, + ac.*, + l.* +from + ENGAGEMENT e, + APPLICATION a, + APPLICATIONCOMPONENT ac, + NORMALIZEDLICENSE l +where + e.ID_ENGAGEMENT = a.PARENT_APPLICATION AND + a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND + ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND + l."normalizedLicenseType" not in ('COMMERCIAL', 'IGNORE') +order by + UPPER("ID_APPLICATION"), -- sort by ID so assuring we have the same order as defined in config + UPPER("groupId"), + UPPER("artifactId"), + UPPER("version"), + UPPER("effectiveNormalizedLicense"), + UPPER("normalizedLicense") \ No newline at end of file diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/scancode_sources.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/scancode_sources.sql index 1062b668..83cd8d15 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/scancode_sources.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/scancode_sources.sql @@ -16,7 +16,8 @@ from NORMALIZEDLICENSE l where a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND - ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE + ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND + l."normalizedLicenseType" not in ('COMMERCIAL') group by "groupId", "artifactId", diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql new file mode 100644 index 00000000..61c91d4b --- /dev/null +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql @@ -0,0 +1,21 @@ +-- SPDX-License-Identifier: Apache-2.0 +-- +-- returns all distinct OSS-Licenses texts +select distinct + GROUP_CONCAT(DISTINCT l."normalizedLicense" ORDER BY "normalizedLicense" ASC SEPARATOR ', ') as "normalizedLicense", + GROUP_CONCAT(DISTINCT CONCAT( ac."artifactId", ' (', ac."version", ')' ) ORDER BY "artifactId" ASC, "version" ASC SEPARATOR ', ') as "artifact", + ARRAY_AGG(DISTINCT l."normalizedLicenseContent" ORDER BY "normalizedLicenseContent" DESC)[1] as "normalizedLicenseContent", + UCASE(REGEXP_REPLACE(l."normalizedLicenseContent",'\s','')) as "unifiedNormalizedLicenseContent" +from + APPLICATION a, + APPLICATIONCOMPONENT ac, + NORMALIZEDLICENSE l +where + a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND + ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND + l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE') AND + l."normalizedLicenseUrl" is not null +group by + "unifiedNormalizedLicenseContent" +order by + "unifiedNormalizedLicenseContent" diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm new file mode 100644 index 00000000..3986dcc4 --- /dev/null +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm @@ -0,0 +1,163 @@ +## template + + + + + +## styling (table) + + + + +

Thirdparty Components

+THE FOLLOWING SETS FORTH ATTRIBUTION NOTICES FOR THIRD PARTY SOFTWARE THAT MAY BE CONTAINED IN PORTIONS OF THE $ENGAGEMENT.getDataRow(0).engagementName PRODUCT. +

+This document has 3 sections: +

    +
  • Component / License overview: Lists all components with their associated licenses, link to the source code and given copyright information
  • +
  • License texts: Gives all license texts applicable for the components
  • +
  • Further attribution notices: Gives further attribution notices applicable to components
  • +
+ +

Component / License overview

+ +The following table lists all third party Open Source Components that may be contained in this product. Besides name and version the table lists: +
    +
  • the URL of the Source-Code-Repository for downloading the source code
  • +
  • the names of the OSS licenses attached to the component; in case that the component is dual-/multilicensed (NA) indicates that the component is herein distributed under an alternative license and the license does not apply in the context of this distribution
  • +
  • copyright information given in the third party component; in case that no direct copyright notices are given then author/contributor information might be given if available
  • +
+ +

+ + ## head row + + + + + + + + ## content + + + #foreach ($license in $NONCOMMERCIALCOMPONENTS) + + #set( $aid = $license.artifactId ) + #set( $gid = $license.groupId ) + #set( $ver = $license.version ) + #set( $apps = $license.APPS ) + ## application component name + ## application component version + + + ## copyrights + + #end + +
NameVersionSource-Core-RepositoryLicensesCopyrights (or Authors/Contributors)
$license.artifactId$license.version$license.ossHomepage + #foreach($ac in $NONCOMMERCIALCOMPONENTS_LICENSES ) + #if( $aid == $ac.artifactId && $gid == $ac.groupId && $ver == $ac.version ) +## #set( $key = "${gid}${aid}${ac.normalizedLicense}" ) ## this is not necessarily sufficient as uinque id because version is missing +## $ac.normalizedLicense ## linking does not work currently + $ac.normalizedLicense + #if( $ac.effectiveNormalizedLicense == "Ignore" && $ac.effectiveNormalizedLicense != $ac.normalizedLicense) + (NA) + #end +
+ #end + #end +
$esc.newLinesAsBreaks($esc.html($license.copyrights))
+ + +

License texts

+ +

+This section lists all license texts that might be applicable for the third party open source components contained in this product. Each section lists: +

    +
  • the unique license text (or text referencing the license)
  • +
  • the licenses given or referenced in this text (see note below)
  • +
  • the components for which this text applies
  • +
+Note that not every license given or referenced in below texts necessarily applies in the +context of the distribution of this product: See the table in Component / +License overview for licenses which might not apply due to selected dual-/multi-licensing +options. +

+ + +#foreach($ul in $UNIQUELICENSES) + #set($nlc = $ul.normalizedLicenseContent) +
+ +

+ #if ($nlc) + + +
+
$esc.html($esc.wrap100to80($nlc))
+
+ #else + Es liegt kein Lizenztext vor. + #end +

+

+Defined/referenced license(s): $ul.normalizedLicense +
+Applicable component(s): $ul.artifact +

+#end +
+
+ +

Attribution Notices

+The following additional attribution notices are given within NOTICE files and are reproduced here as required in the license conditions. + +#foreach($nf in $NOTICEFILES) +
+

+Component(s): $nf.artifact +

+

+ #if ($nf.noticeFileContent) + + +
+
$esc.html($nf.noticeFileContent)
+
+ #else + Es liegt kein Notice-File vor + #end +

+ +#end + + + + \ No newline at end of file diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm index caf3b28e..2355ecb8 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm @@ -1,3 +1,4 @@ +scancodeoptions="--processes 16 --copyright --only-findings --license --license-text --consolidate" beginning=$(pwd) mkdir scancodeOutput so=$(pwd)/scancodeOutput @@ -6,18 +7,18 @@ dir=$(pwd)/Source #foreach ($artifact in $ARTIFACTS) if [ ! -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] -then + then if [ -d $dir/$purlhandler.pathFor($artifact.packageUrl)/sources ] - then - scancode -c -n 16 --json-pp $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json --only-findings $dir/$purlhandler.pathFor($artifact.packageUrl)/sources - fi + then + scancode $scancodeoptions --json-pp $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json $dir/$purlhandler.pathFor($artifact.packageUrl)/sources + fi cat $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json >> $so/scancodeOut.json if [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] - then + then printf "\n," >> $so/scancodeOut.json - fi -fi -#end + fi + fi + #end printf "END" >> $so/scancodeOut.json grep -v ",END" $so/scancodeOut.json > $so/scancodeOutput.json rm $so/scancodeOut.json diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm index c6d964db..07a76339 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm @@ -5,10 +5,8 @@ # # In order to use this function properly, a correct setup of scancode-toolkit is necessary. Download and install the newest version of scancode here: https://github.com/nexB/scancode-toolkit/releases # It is important to note that scancode has to be put in the PATH variable and you are able to call the tool from everywhere in your system. -# After setting it up correctly, you just have to run the generated "scancode_projectname.sh" script to automatically create a list of copyrights and their holders called scancodeOutput.json -# Furthermore a folder "Source" gets created which contains all source files of downloaded artifacts and the individual scancode output of the artifacts. -# All artifacts that could not be downloaded are listed in need-to-retrieve-manually.txt. If further scancode-toolkit scans are necessary after the source download has been completed once, -# you can also just run the scancodeScan.sh script. +# After setting it up correctly, you just have to run the generated "scancode_projectname.sh" script to automatically download package sources, scan them via scancode and store +# the results in directory tree within folder "Source". mkdir Source cd Source diff --git a/core/src/main/resources/starters/rules/LicenseAssignmentProject.xls b/core/src/main/resources/starters/rules/LicenseAssignmentV2Project.xls similarity index 79% rename from core/src/main/resources/starters/rules/LicenseAssignmentProject.xls rename to core/src/main/resources/starters/rules/LicenseAssignmentV2Project.xls index b957b1771d8b5933c43331da9c87097b64c4c48e..c0ebdbbba9723c81fa7107cf69fce0df1c8f2f78 100644 GIT binary patch delta 3375 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 delta 3655 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 { + handler.pathFor("pkg:maven/com.someorg/some..prod@4.5.35"); + }); + } } diff --git a/core/src/test/resources/scancode/curations.json b/core/src/test/resources/scancode/curations.json new file mode 100644 index 00000000..bf9da71f --- /dev/null +++ b/core/src/test/resources/scancode/curations.json @@ -0,0 +1,2 @@ +{"artifacts": [ +] } \ No newline at end of file diff --git a/documentation/files/application.properties b/documentation/files/application.properties index a399ea39..874da74d 100644 --- a/documentation/files/application.properties +++ b/documentation/files/application.properties @@ -41,6 +41,20 @@ solicitor.classpath-guessedlicenseurl-cache-locations=licenseurls # Deprecated features are deactivated by default. If set to true they might be (temporarily) activated. solicitor.deprecated-features-allowed=false +## Feature flags for activation of non-standard/experimental functionality +# Incorporate scancode infos into model +solicitor.feature-flag.scancode=false + +## Parameters for controlling the processing of scancode information +# minimum score of detected license findings to be taken into account for Solicitor processing +solicitor.scancode.min-license-score=80.0 +# minimum "percentage_of_license_text" for a file to be possibly taken as license text +solicitor.scancode.min-licensefile-percentage=50.0 +# filename of the curationy.yaml file +solicitor.scancode.curations-filename=output/curations.yaml +# base path of the file repo where sources and scancode information is stored +solicitor.scancode.repo-base-path=output/Source + # If there is an exception while reading the raw inventory data for an application the processing will be aborted by default. # By setting this property to true processing will continue in case that the input file is not found. This allows processing # in multi application projects where some input files are not yet available. A warning (SOLI-045) will be logged in this case and diff --git a/documentation/files/solicitor_base.cfg b/documentation/files/solicitor_base.cfg index f65c6da1..7e4c9a8c 100644 --- a/documentation/files/solicitor_base.cfg +++ b/documentation/files/solicitor_base.cfg @@ -23,84 +23,93 @@ "rules" : [ { "type" : "dt", "optional" : true, - "ruleSource" : "file:${cfgdir}/rules/LicenseAssignmentProject.xls", + "ruleSource" : "file:${cfgdir}/rules/LicenseAssignmentProject", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt", "ruleGroup" : "LicenseAssignmentProject", + "description" : "setting license in case that no one was detected or overwriting a wrongly detected license (project overrides; 'old' rule structure)", + "deprecationWarnOnly" : true, + "deprecationDetails" : "Use of decision table LicenseAssignmentProject is deprecated, use LicenseAssignmentV2Project instead. See release notes of release 1.4.0 for migration hints" + },{ + "type" : "dt", + "optional" : true, + "ruleSource" : "file:${cfgdir}/rules/LicenseAssignmentV2Project", + "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt", + "ruleGroup" : "LicenseAssignmentV2Project", "description" : "setting license in case that no one was detected or overwriting a wrongly detected license (project overrides)" },{ "type" : "dt", "optional" : false, - "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseAssignmentSample.xls", - "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt", - "ruleGroup" : "LicenseAssignmentDefaults", + "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample", + "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt", + "ruleGroup" : "LicenseAssignmentV2Defaults", "description" : "setting license in case that no one was detected or overwriting a wrongly detected license" },{ "type" : "dt", "optional" : true, - "ruleSource" : "file:${cfgdir}/rules/LicenseNameMappingProject.xls", + "ruleSource" : "file:${cfgdir}/rules/LicenseNameMappingProject", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseNameMapping.drt", "ruleGroup" : "LicenseNameMappingProject", "description" : "mapping different spelling of license name to the SPDX-ID format (project overrides)" },{ "type" : "dt", "optional" : false, - "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls", + "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseNameMappingSample", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseNameMapping.drt", "ruleGroup" : "LicenseNameMappingDefaults", "description" : "mapping different spelling of license name to the SPDX-ID format" },{ "type" : "dt", "optional" : true, - "ruleSource" : "file:${cfgdir}/rules/MultiLicenseSelectionProject.xls", + "ruleSource" : "file:${cfgdir}/rules/MultiLicenseSelectionProject", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/MultiLicenseSelection.drt", "ruleGroup" : "MultiLicenseSelectionProject", "description" : "ignoring non applicable multi licensing alternatives (project overrides)" },{ "type" : "dt", "optional" : false, - "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/MultiLicenseSelectionSample.xls", + "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/MultiLicenseSelectionSample", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/MultiLicenseSelection.drt", "ruleGroup" : "MultiLicenseSelectionDefaults", "description" : "ignoring non applicable multi licensing alternatives" },{ "type" : "dt", "optional" : true, - "ruleSource" : "file:${cfgdir}/rules/LicenseSelectionProject.xls", + "ruleSource" : "file:${cfgdir}/rules/LicenseSelectionProject", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseSelection.drt", "ruleGroup" : "LicenseSelectionProject", "description" : "selecting the effective license (project overrides)" },{ "type" : "dt", "optional" : false, - "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseSelectionSample.xls", + "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LicenseSelectionSample", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseSelection.drt", "ruleGroup" : "LicenseSelectionDefaults", "description" : "selecting the effective license" },{ "type" : "dt", "optional" : true, - "ruleSource" : "file:${cfgdir}/rules/LegalPreEvaluationProject.xls", + "ruleSource" : "file:${cfgdir}/rules/LegalPreEvaluationProject", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LegalPreEvaluation.drt", "ruleGroup" : "LegalPreEvaluationProject", "description" : "legal preevaluation based on the standards defined in some generic policy (project overrides)" },{ "type" : "dt", "optional" : false, - "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LegalPreEvaluationSample.xls", + "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LegalPreEvaluationSample", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LegalPreEvaluation.drt", "ruleGroup" : "LegalPreEvaluationDefaults", "description" : "legal preevaluation based on the standards defined in some generic policy" },{ "type" : "dt", "optional" : true, - "ruleSource" : "file:${cfgdir}/rules/LegalEvaluationProject.xls", + "ruleSource" : "file:${cfgdir}/rules/LegalEvaluationProject", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LegalEvaluation.drt", "ruleGroup" : "LegalEvaluationProject", "description" : "final legal evaluation (project overrides)" },{ "type" : "dt", "optional" : false, - "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LegalEvaluationSample.xls", + "ruleSource" : "classpath:com/devonfw/tools/solicitor/rules/LegalEvaluationSample", "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LegalEvaluation.drt", "ruleGroup" : "LegalEvaluationDefaults", "description" : "final legal evaluation - THESE ARE DUMMY RULES" @@ -150,6 +159,19 @@ "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents_guessedlicenses.sql", "UNIQUELICENSES" : "classpath:com/devonfw/tools/solicitor/sql/uniqueguessedlicenses.sql" } + },{ + "type" : "velo", + "templateSource" : "file:${cfgdir}/templates/Pflichtangaben.vm", + "target" : "${cfgdir}/output/Pflichtangaben_${project}.html", + "description" : "Das Dokument mit Pflichtangaben", + "dataTables" : { + "ENGAGEMENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_engagements.sql", + "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql", + "UNIQUELICENSES" : "classpath:com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql", + "NOTICEFILES" : "classpath:com/devonfw/tools/solicitor/sql/noticefiles.sql", + "NONCOMMERCIALCOMPONENTS" : "classpath:com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql", + "NONCOMMERCIALCOMPONENTS_LICENSES" : "classpath:com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql" + } },{ "type" : "velo", "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Solicitor_Diff_Template_Sample.vm", @@ -189,7 +211,7 @@ },{ "type" : "velo", "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm", - "target" : "${cfgdir}/output/scancodeScan.cmd", + "target" : "${cfgdir}/output/scancodeScan.sh", "description" : "Script for running and copying scancode output to directory", "dataTables" : { "MODELROOT" : "classpath:com/devonfw/tools/solicitor/sql/modelroot.sql", diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 862e638c..fb996492 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -149,6 +149,9 @@ The internal business data model consists of 6 entities: | version | String | component identifier: Version | repoType | String | component identifier: RepoType | packageUrl | String | the https://github.com/package-url/purl-spec[Package URL] as an technology neutral component identifier +| noticeFileUrl | String | URL referencing a NOTICE file to be included in the attributions (optional, see <>) +| noticeFileContent | String | resolved content of noticeFileUrl (optional, see <>) +| copyrights | String | Copyright statements found in the components metadata / code (optional, see <>) |=== ==== RawLicense @@ -158,6 +161,7 @@ The internal business data model consists of 6 entities: | declaredLicense | String | name of the declared license | licenseUrl | String | URL of the declared license | trace | String | detail info of history of this data record +| origin | String | origin of the raw license data; either the lowercase classname of the Reader or "scancode" if licensedata was taken from scancode results | specialHandling | boolean | _(for controlling rule processing)_ |=== @@ -171,6 +175,7 @@ The internal business data model consists of 6 entities: | normalizedLicenseType | String | type of the license, see <> | normalizedLicense | String | name of the license in normalized form (SPDX-Id) or special "pseudo license id", see <> | normalizedLicenseUrl | String | URL pointing to a normalized form of the license +| normalizedLicenseContent | String | resolved content of normalizedLicenseUrl | normalizedLicenseType | String | type of the license, see <> | effectiveNormalizedLicenseType | String | type of the effective license, see <>| effectiveNormalizedLicense | String | effective normalized license (SPDX-Id) or "pseudo license id"; this is the information after selecting the right license in case of multi licensing or any license override due to a component being redistributed under a different license | effectiveNormalizedLicenseUrl | String | URL pointing to the effective normalized license @@ -354,10 +359,12 @@ They are defined as a sequence of rule templates and corresponding XLS (or CSV) "rules" : [ { "type" : "dt", <1> "optional" : false, <2> - "ruleSource" : "classpath:samples/LicenseAssignmentSample.xls", <3> <7> - "templateSource" : "classpath:com/.../rules/rule_templates/LicenseAssignment.drt", <4> <7> - "ruleGroup" : "LicenseAssignment", <5> - "description" : "setting license in case that no one was detected" <6> + "ruleSource" : "classpath:samples/LicenseAssignmentV2Sample.xls", <3> <9> + "templateSource" : "classpath:com/.../rules/rule_templates/LicenseAssignmentV2.drt", <4> <9> + "ruleGroup" : "LicenseAssignmentV2", <5> + "description" : "setting license in case that no one was detected", <6> + "deprecationWarnOnly" : true, <7> + "deprecationDetails" : "This decision table should be migrated to ..." <8> }, . . @@ -381,7 +388,10 @@ resources by appending suffixes _xls_ and _csv_. <4> location of the drools rule template to be used to define the rules together with the decision table data <5> id of the group of rules; used to reference it e.g. when doing logging <6> some textual description of the rule group -<7> _placeholder patterns might be used here_ +<7> flag to control which level of deprecation (see <>) applies to this rule group; optional and only applicable if `deprecationDetails` is also defined. +<8> optional value; if set then the use of the defined decision table is deprecated; the +given string will be given as part of the log message +<9> _placeholder patterns might be used here_ When running, _Solicitor_ will execute the rules of each rule group separately and in the order given by the configuration. Only if there are no more rules to fire in a group _Solicitor_ will @@ -422,7 +432,7 @@ additionally to the default writers. The section `additionalWriters` has the sam [listing] "additionalWriters" : [ { - "type" : + "type" : ... "dataTables" : { ... @@ -591,7 +601,7 @@ These configurations may also be used to overwrite options of a https://commons. Important: In case that a component has multiple licenses attached, there needs to be a separate line in the csv file for each license. -WARNING: The CSV reader currently does not fill the attribute `packageUrl`. Any functionality/reporting based on this attribute will be disfunctional for data read bythe CSV reader. +WARNING: The CSV reader currently does not fill the attribute `packageUrl`. Any functionality/reporting based on this attribute will be disfunctional for data read bythe CSV reader. === NPM @@ -887,6 +897,8 @@ The phase itself consists of two decision tables / rule groups: ==== Decision Table: Explicitely setting Licenses With this decision table is is possible to explicitely assign NormalizedLicenses to components. This will be used if the imported RawLicense data is either incomplete or incorrect. Items which have been processed by rules of this group will not be reprocessed by the next rule group. +Decision table data: `LicenseAssignmentV2*.xls/csv` + * LHS conditions: ** `Engagement.clientName` ** `Engagement.engagementName` @@ -894,6 +906,7 @@ With this decision table is is possible to explicitely assign NormalizedLicenses ** `ApplicationComponent.groupId` icon:magic[] ** `ApplicationCompomnent.artifactId` icon:magic[] ** `ApplicationComponent.version` icon:magic[] +** `RawLicense.origin` icon:magic[] (new with "V2" version of rules) ** `RawLicense.declaredLicense` icon:magic[] ** `RawLicense.url` icon:magic[] @@ -907,9 +920,17 @@ icon:magic[]: On these fields the <> might be used All RawLicenses which are in scope of fired rules will be marked so that they do not get reprocessed by the following decision table. +NOTE: With the "V2" version of rules the additional field/condition `origin` was introduced. +This can be used to fire rules only if the raw license data was obtained from a specific data source. +Its primary intention is to distinguish between data obtained via normal readers or from Scancode data. +Decision table data for the new data structure is named `LicenseAssignmentV2*.xls/csv`. +The old decision table structure `LicenseAssignment*.xls/csv` is deprecated but for compatibility reasons still supported. + ==== Decision Table: Detecting Licenses from Imported Data With this decision table the license info from the RawLicense is mapped to the NormalizedLicense. This is based on the name and/or URL of the license as imported via the readers. +Decision table data: `LicenseNameMapping*.xls/csv` + * LHS conditions: ** `RawLicense.declaredLicense` icon:magic[] ** `RawLicense.url` icon:magic[] @@ -928,6 +949,8 @@ This phase consists of two decision tables. ==== Choosing specific License in case of Multi-Licensing This group of rules has the speciality that it might match to a group of NormalizedLicenses associated to an ApplicationComponent. In case that multiple licenses are associated to an ApplicationComponent one of them might be selected as "effective" license and the others might be marked as `Ignored`. +Decision table data: `MultiLicenseSelection*.xls/csv` + * LHS conditions: ** `ApplicationComponent.groupId` icon:magic[] ** `ApplicationComponent.artifactId` icon:magic[] @@ -949,6 +972,8 @@ It is important to note that the rules only match, if all licenses given in the The second decision table in this group is used to define the `effectiveNormalizedLicense` (if not already handled by the decision table before). +Decision table data: `LicenseSelection*.xls/csv` + * LHS conditions: ** `ApplicationComponent.groupId` icon:magic[] ** `ApplicationComponent.artifactId` icon:magic[] @@ -970,6 +995,8 @@ The third phase ist the legal evaluation of the licenses and the check, whether ==== Pre-Evaluation based on common rules Within the pre evaluation the license info is checked against standard OSS usage policies. This roughly qualifies the usage and might already determine licenses which are OK in any case or which need to be further evaluated. Furtheron they qualify whether the license text or source code needs to be included in the distribution. The rules in this decision table are only based on the `effectiveNormalizedLicense` and do not consider any project, application of component information. +Decision table data: `LegalPreEvaluation*.xls/csv` + * LHS condition: ** `NormalizedLicense.effectiveNormalizedLicenseType` ** `NormalizedLicense.effectiveNormalizedLicense` @@ -986,6 +1013,8 @@ Within the pre evaluation the license info is checked against standard OSS usage The decision table for final legal evaluation defines all rules which are needed to create the result of the legal evaluation. Rules here might be general for all projects or even very specific to a project if the rule can not be applied to other projects. +Decision table data: `LegalEvaluation*.xls/csv` + * LHS condition: ** `Engagement.clientName` ** `Engagement.engagementName` @@ -1220,6 +1249,122 @@ The following features are deprecated via the above mechanism: * Reader of type "gradle" (use Reader of type "gradle2" instead); Stage 2 from Version 1.0.5 on; see https://github.com/devonfw/solicitor/issues/58 * Reader of type "npm" (use type "npm-license-crawler-csv" instead); Stage 1 from Version 1.0.8 on; see https://github.com/devonfw/solicitor/issues/62 * "REGEX:" prefix notation in rule templates (use "(REGEX)" suffix instead); Stage 1 from Version 1.3.0 on; see https://github.com/devonfw/solicitor/issues/78 +* Use of `LicenseAssignmentProject.xls` decision table (use `LicenseAssignmentV2Project.xls` instead); Stage 1 from Version 1.4.0 on + +== Experimental Scancode Integration + +Starting from version 1.4.0 _Solicitor_ can be integrated with the tool https://github.com/nexB/scancode-toolkit[ScanCode] to include detailed information gathered from +the "deep license scan" performed by ScanCode. This includes detected Licenses, Copyrights and Notice-Files. + +WARNING: The current integration with ScanCode is experimental: The used ScanCode parameters, interfacing and curations logic and all parts of the data persistence are experimental and thus might result in insufficient quality of results. The current workflow and implementation is subject to change in future versions without further notice. + +=== General workflow +The general workflow when integrating with ScanCode consists of the following 3 steps: + +. Execute _Solicitor_ in a "classic" way i.e. just based on the data provided via the Readers as described in +<>. Besides the normal reports/documents generated this will also +create scripts for downloading the needed OSS source codes and run Scancode. +. Download source codes and run ScanCode by executing the generated scripts. The downloadad sources and ScanCode results will be saved to a directory tree in the local filesytem. +. Execute _Solicitor_ a second time. For all ApplicationComponents where ScanCode information is available +(stored in the local directory tree) the license data as obtained from the Readers is replaced by this information. The data model is enriched with the found copyright and notice file information. Reports (see <>) are now based on the ScanCode data (where available). + +=== Prerequisites + +==== Bash +The scripts generated by _Solicitor_ to download sources and run ScanCode are in Bash syntax. +So either run it on a system using natively Bash (linux) or install an appropriate environment (e.g. Git Bash) if you are using a windows environment. + +==== ScanCode +Download and install ScanCode from https://github.com/nexB/scancode-toolkit/releases. +Make sure that the executable is included in the search PATH for executables. + +==== Activate feature +As the ScanCode integration is still experimental it is currently deactivated by default. +To enable it set system property `solicitor.feature-flag.scancode=true`. +(See <> for information how to do so.) +If this feature flag is not activated then _Solicitor_ will not try to attempt to read ScanCode information from the local file system. + +=== Detailed workflow +==== Solicitor 1st run +Execute _Solicitor_ in a classic way. As part of the report creation step this will generate two scripts: + +* `output/scancode_PROJECTNAME.sh` (for downloading the sources, also calls `scancodeScan.sh`) +* `output/scancodeScan.sh` (for running ScanCode on the downloaded sources) + +Scripts will include all ApplicationComponents with exception of those where `normalizedLicenseType` was set to `COMMERCIAL`. + +==== Download Sources and run Scancode +Change to directory `output` and execute `sh scancode_PROJECTNAME.sh`. +This will download all sources and process them via ScanCode. +This might take several hours to complete. +Results are stored in subdirectory `Source` of the directory `output` and is organized in a tree structure given by the https://github.com/package-url/purl-spec[PackageURL] of the ApplicationComponents. + +==== Solicitor 2nd run +Execute _Solicitor_ a second time. +After reading the component/license information from the Readers (but before starting the rule engine) +_Solicitor_ will try to look up ScanCode information from the directory tree in `output/Sources` for all processed ApplicationComponents. If information is found for an ApplicationsComponent the following is done: + +* License information (including URL of license text) as obtained from the Readers is replaced by the license info found by ScanCode +* Copyrights are taken from ScanCode results +* Info on NOTICE file is taken from the ScanCode results +* If the ScanCode results contain information about a project URL then this is stored as `ossHomepage` + +==== Output +Main target of the additional information obtained from ScanCode is currently the new report `Attributions_PROJECTNAME.html` which lists + +* all ApplicationComponents (excluding those which are not OSS licensed) +* with all found copyrights +* and all licenses +* including all different license texts +* and contents of all found NOTICE files + +=== Correcting data +The data obtained from ScanCode might be affected by false positives (wrongly detected a license or copyright) or false negatives (missed to detect a license or copyright). +To compensate such defects there are two mechanisms: +Applying Curation information from a "curations" file or changing the License information via the decision table rules. + +==== Curations file +To define curations you might create a file `output/curations.yaml` containing the following structure: + +[source,yaml] +---- +artifacts: + - name: pkg/npm/@somescope/somepackage/1.2.3 <1> + url: https://github.com/foo/bar <2> + licenses: <3> + - license: MIT <4> + url: https://raw.githubusercontent.com/foo/bar/LICENSE <5> + copyrights: <6> + - (c) 2021 Donald Duck <7> + - "(c) 2019 Mickey Mouse " <8> + - name: pkg/npm/@anotherscope/anotherpackage/4.5.6 <9> +. +. +. +---- +<1> Path of the package information as used in the file tree. Derived from the PackageURL. +<2> URL of the project, will be stored as `ossHomepage`. (Optional: no change if not existing.) +<3> Licenses to set. Optional. If defined then all found licenses will be replaced by the list of licenses given here. +<4> SPDX identifier of license. +<5> URL pointing to license text. +<6> Copyrights to set. Optional. If defined then all found copyrights will be replaced by the list of copyrights given here. +<7> A single copyright. +<8> Another copyright. Note that due to YAML syntax any string containing `:` needs to be enclosed with parantheses +<9> Further packages to follow. + +==== Decision table rules +As for license information obtained from the Readers the license information from ScanCode can also be altered using decision table rules. A new attribute `origin` was introduced in the `RawLicense` entity as well as condition field in decision table `LicenseAssignmentV2*.xls/csv`. The `origin` attribute in `Rawlicense` either contains the string `scancode` if the license information came from ScanCode or it contains the (lowercase) class name of the used Reader. + +Using the <> it is possible to qualify whether a rule should apply for licenses found by ScanCode or not: + +[cols="1,2",options="header"] +|==================== +|Value of condition Origin | rule applies for ... +|`scancode` | ... licenses obtained from ScanCode information +|`NOT:scancode` | ... licenses obtained from normal Readers +| _(empty)_ | ... in both cases +|==================== + [appendix] == Default Base Configuration @@ -1229,7 +1374,7 @@ of the _Solicitor_ configuration file which will be used if the project specific [source,json] .Default Configuration ---- -include::files/solicitor_base.cfg[lines=23..169] +include::files/solicitor_base.cfg[lines=23..220] ---- [appendix] @@ -1280,12 +1425,16 @@ Similar to the above but uses guessed license URLs and content, see <>. + +NOTE: Generating these scripts is an experimental feature and might be changed or removed in future versions without any notice. -NOTE: This report is an experimental feature and might be changed or removed in future versions without any notice. +=== Attributions.vm +This template creates an attributions document which lists all used OSS components with their licenses, license texts and found copyrights information as well as found information from NOTICE files. +The template is part of the <> and requires ScanCode to be used to collect all necessary information. [appendix] == Extending Solicitor @@ -1337,6 +1486,10 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar >. +* New decision table structure `LicenseAssignmentV2` with additional condition `origin`. +Old structure deprecated but still supported. +Migrate existing project decision tables by renaming `LicenseAssignmentProject.xls` to `LicenseAssignmentV2Project.xls` and introducing a new (empty) column `Origin` between existing columns `OSS Version` and `Declared License`. * Added Solicitor Logo and code for creating variants / animation. * https://github.com/devonfw/solicitor/issues/117: New attribute `packageUrl` in `ApplicationComponent`. * Experimental scancode-toolkit integration changed to using Bash scripting. From f49ee49c18b324204e46b39bdbdde16856237526 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 3 Jun 2022 01:31:26 +0200 Subject: [PATCH 002/139] changed property to reflect renaming in new version of spring boot --- core/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index 874da74d..8d56c0fb 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -10,7 +10,7 @@ logging.level.org.drools=WARN logging.level.com.zaxxer.hikari.util.DriverDataSource=ERROR # logging.level.com.devonfw.tools.solicitor.ruleengine.drools.AuditEntryBuilder=DEBUG -logging.file=solicitor.log +logging.file.name=solicitor.log logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx} From 0f4b8962cbd21d996df378c352a720a52d280126 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 3 Jun 2022 10:50:50 +0200 Subject: [PATCH 003/139] Replace isBlank() by isEmpty() to keep compatibility with Java 8 --- .../com/devonfw/tools/solicitor/reader/yarn/YarnReader.java | 2 +- .../tools/solicitor/ruleengine/drools/DroolsRuleEngine.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java index ca6b5461..95128b43 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java @@ -76,7 +76,7 @@ public void readInventory(String type, String sourceUrl, Application application // check whether VendorUrl is included in input file or not if (attributes.size() == 6) { - if (attributes.get(4) != null && !attributes.get(4).isBlank()) { + if (attributes.get(4) != null && !attributes.get(4).isEmpty()) { homePage = attributes.get(4); } else { homePage = repo; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java index 41778355..cc4d3eac 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java @@ -101,7 +101,7 @@ private int executeRuleGroup(ModelRoot modelRoot, RuleConfig rc) { } // check if this rule group was marked as deprecated - if (rc.getDeprecationDetails() != null && !rc.getDeprecationDetails().isBlank()) { + if (rc.getDeprecationDetails() != null && !rc.getDeprecationDetails().isEmpty()) { this.deprecationChecker.check(rc.isDeprecationWarnOnly(), rc.getDeprecationDetails()); } From b17b10cf754fdf38b2b77216b1120383278bcb83 Mon Sep 17 00:00:00 2001 From: pgarus <35223024+pgarus97@users.noreply.github.com> Date: Fri, 2 Sep 2022 15:21:26 +0200 Subject: [PATCH 004/139] added stability / data corruption safety for bash scripts of scancode integration (#122) * added stability / corruption safety for sources and scancode scan results * added feature to changelog * added missing "then" command --- .../solicitor/templates/ScancodeScanScript.vm | 26 +++++++++++-------- .../solicitor/templates/ScancodeScript.vm | 7 +++++ documentation/master-solicitor.asciidoc | 1 + 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm index 2355ecb8..3229134a 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm @@ -6,19 +6,23 @@ echo {\"artifacts\": [ > scancodeOutput/scancodeOut.json dir=$(pwd)/Source #foreach ($artifact in $ARTIFACTS) -if [ ! -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] - then - if [ -d $dir/$purlhandler.pathFor($artifact.packageUrl)/sources ] - then +if [ ! -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] || [ ! -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed ] +then + rm $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed + rm $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json + if [ -d $dir/$purlhandler.pathFor($artifact.packageUrl)/sources ] && [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/sources.completed ] + then scancode $scancodeoptions --json-pp $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json $dir/$purlhandler.pathFor($artifact.packageUrl)/sources - fi + touch $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed + fi +fi +if [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] && [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed ] +then cat $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json >> $so/scancodeOut.json - if [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] - then - printf "\n," >> $so/scancodeOut.json - fi - fi - #end + printf "\n," >> $so/scancodeOut.json +fi +#end + printf "END" >> $so/scancodeOut.json grep -v ",END" $so/scancodeOut.json > $so/scancodeOutput.json rm $so/scancodeOut.json diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm index 07a76339..3ae23913 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm @@ -20,7 +20,13 @@ echo "These artifacts need to be downloaded manually:" > need-to-retrieve-manual if [ ! -d "$purlhandler.pathFor($artifact.packageUrl)" ] then mkdir -p $purlhandler.pathFor($artifact.packageUrl) + fi + if [ ! -f "$purlhandler.pathFor($artifact.packageUrl)/sources.completed" ] || [ ! -d "$purlhandler.pathFor($artifact.packageUrl)/sources" ] + then cd $purlhandler.pathFor($artifact.packageUrl) + rm sources.completed + rm -r sources + rm sources.jar curl -# $purlhandler.sourceDownloadUrlFor($artifact.packageUrl) -o sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) #if ( $purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) == "jar" ) unzip -q -d sources sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) @@ -41,6 +47,7 @@ echo "These artifacts need to be downloaded manually:" > need-to-retrieve-manual #[[cd $SD]]# echo "Artifact $purlhandler.sourceDownloadUrlFor($artifact.packageUrl)" >> need-to-retrieve-manually.txt else + touch sources.completed #[[cd $SD]]# fi fi diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index fb996492..63127628 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1486,6 +1486,7 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar >. * New decision table structure `LicenseAssignmentV2` with additional condition `origin`. Old structure deprecated but still supported. From 3866e5917a147a327197ed4258a6f9d75ab8fc7f Mon Sep 17 00:00:00 2001 From: pgarus <35223024+pgarus97@users.noreply.github.com> Date: Mon, 5 Sep 2022 09:49:38 +0200 Subject: [PATCH 005/139] deprecated usage of npm-license-checker reader and added documentation (#127) Co-authored-by: Oliver Hecker <8004361+ohecker@users.noreply.github.com> --- .../NpmLicenseCrawlerReader.java | 10 +++- documentation/master-solicitor.asciidoc | 49 ++++++++++--------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java index 6099037c..4b303d7d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java @@ -25,10 +25,16 @@ import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; import com.devonfw.tools.solicitor.reader.AbstractReader; import com.devonfw.tools.solicitor.reader.Reader; +import com.devonfw.tools.solicitor.reader.npmlicensechecker.NpmLicenseCheckerReader; /** * A {@link Reader} which reads data produced by the NPM * License Crawler. + *

+ * This reader requires a specific version of license-checker which is not released in the official npm repositories but is only + * available via Github. This might result in difficulties in environments which have only limited access to internet resources. + * Additionally, developer dependencies cannot be excluded as the --production option seemingly does not work properly. + * Use {@link NpmLicenseCheckerReader} instead. */ @Component public class NpmLicenseCrawlerReader extends AbstractReader implements Reader { @@ -62,7 +68,9 @@ public Set getSupportedTypes() { @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, String repoType, Map configuration) { - + this.deprecationChecker.check(true, + "Use of Reader of type '"+ SUPPORTED_TYPE +"' is deprecated, use 'npm-license-checker' instead. See https://github.com/devonfw/solicitor/issues/125"); + if (SUPPORTED_TYPE_DEPRECATED.equals(type)) { this.deprecationChecker.check(true, "Use of type 'npm' is deprecated. Change type in config to '" + SUPPORTED_TYPE + "'. See https://github.com/devonfw/solicitor/issues/62"); diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 63127628..6ab2d6d9 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -605,75 +605,77 @@ WARNING: The CSV reader currently does not fill the attribute `packageUrl`. Any === NPM -For NPM based projects either the NPM License Crawler (https://www.npmjs.com/package/npm-license-crawler) or the NPM License Checker (https://www.npmjs.com/package/license-checker) might be used. The NPM License Crawler can process several node packages in one run. +For NPM based projects, the NPM License Checker (https://www.npmjs.com/package/license-checker) plugin can be used. The NPM License Crawler plugin is deprecated. -==== NPM License Crawler +==== NPM License Checker -To install the NPM License Crawler the following command needs to be executed. +To install the NPM License Checker the following command needs to be executed. ---- -npm i npm-license-crawler -g +npm i license-checker -g ---- -To get the licenses, the crawler needs to be executed like the following example +To get the licenses, the checker needs to be executed like the following example. We require JSON output here with "--json" and developer dependencies can/should be excluded with "--production". ---- -npm-license-crawler --dependencies --csv licenses.csv +license-checker --production --json > /path/to/licenses.json ---- -The export should look like the following (The csv file is "," separated) +The export should look like the following [source] ---- -include::files/licenses.csv[] +include::files/licensesNpmLicenseChecker.json[] ---- -Source: https://www.npmjs.com/package/npm-license-crawler +Source: https://www.npmjs.com/package/license-checker In _Solicitor_ the data is read with the following part of the config ---- "readers" : [ { - "type" : "npm-license-crawler-csv", - "source" : "file:path/to/licenses.csv", + "type" : "npm-license-checker", + "source" : "file:path/to/licenses.json", "usagePattern" : "DYNAMIC_LINKING", "repoType" : "npm" } ] ---- -==== NPM License Checker +==== NPM License Crawler -To install the NPM License Checker the following command needs to be executed. +WARNING: This reader is deprecated and should no longer be used. It requires a specific dependency (license-checker) which is not available on official npm repositories anymore and scans additional developer dependencies. Use <> (with --production option) instead. See <>. + +To install the NPM License Crawler the following command needs to be executed. ---- -npm i license-checker -g +npm i npm-license-crawler -g ---- -To get the licenses, the checker needs to be executed like the following example (we require JSON output here) +To get the licenses, the crawler needs to be executed like the following example ---- -license-checker --json > /path/to/licenses.json +npm-license-crawler --dependencies --csv licenses.csv ---- -The export should look like the following +The export should look like the following (The csv file is "," separated) [source] ---- -include::files/licensesNpmLicenseChecker.json[] +include::files/licenses.csv[] ---- -Source: https://www.npmjs.com/package/license-checker +Source: https://www.npmjs.com/package/npm-license-crawler In _Solicitor_ the data is read with the following part of the config ---- "readers" : [ { - "type" : "npm-license-checker", - "source" : "file:path/to/licenses.json", + "type" : "npm-license-crawler-csv", + "source" : "file:path/to/licenses.csv", "usagePattern" : "DYNAMIC_LINKING", "repoType" : "npm" } ] @@ -1246,8 +1248,9 @@ wrong/misleading output) the first stage of deprecation will be skipped. === List of Deprecated Features The following features are deprecated via the above mechanism: +* Reader of type "npm-license-crawler-csv" (use Reader of type "npm-license-checker" instead); Stage 1 from Version 1.4.0 on; see https://github.com/devonfw/solicitor/issues/125 * Reader of type "gradle" (use Reader of type "gradle2" instead); Stage 2 from Version 1.0.5 on; see https://github.com/devonfw/solicitor/issues/58 -* Reader of type "npm" (use type "npm-license-crawler-csv" instead); Stage 1 from Version 1.0.8 on; see https://github.com/devonfw/solicitor/issues/62 +* Reader of type "npm"; Stage 1 from Version 1.0.8 on; see https://github.com/devonfw/solicitor/issues/62 * "REGEX:" prefix notation in rule templates (use "(REGEX)" suffix instead); Stage 1 from Version 1.3.0 on; see https://github.com/devonfw/solicitor/issues/78 * Use of `LicenseAssignmentProject.xls` decision table (use `LicenseAssignmentV2Project.xls` instead); Stage 1 from Version 1.4.0 on @@ -1486,6 +1489,8 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar >. * New decision table structure `LicenseAssignmentV2` with additional condition `origin`. From 02cc175972697eb0767f94c5f97afaa65a855f14 Mon Sep 17 00:00:00 2001 From: pgarus <35223024+pgarus97@users.noreply.github.com> Date: Mon, 5 Sep 2022 09:55:27 +0200 Subject: [PATCH 006/139] Scancode Release v31 Integration (#128) * added check for new scancode API in result json for copyright scans * added changelog for new scancode API integration Co-authored-by: Oliver Hecker <8004361+ohecker@users.noreply.github.com> --- .../tools/solicitor/scancode/ScancodeFileAdapter.java | 8 ++++++-- documentation/master-solicitor.asciidoc | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java index 791948b5..3db81035 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java @@ -153,7 +153,11 @@ private ComponentScancodeInfos determineScancodeInformation(String packageUrl) t + this.packageURLHandler.pathFor(packageUrl) + "/" + file.get("path").asText(), 100.0); } for (JsonNode cr : file.get("copyrights")) { - componentScancodeInfos.addCopyright(cr.get("value").asText()); + if(cr.has("copyright")) { + componentScancodeInfos.addCopyright(cr.get("copyright").asText()); + } else { + componentScancodeInfos.addCopyright(cr.get("value").asText()); + } } for (JsonNode li : file.get("licenses")) { @@ -248,7 +252,7 @@ private void applyCurations(String packageUrl, ComponentScancodeInfos componentS * * @param packageUrl package url of the package * @param licenseFilePath the original path or URL - * @return the adjustes patjh or url as a url + * @return the adjusted path or url as a url */ private String normalizeLicenseUrl(String packageUrl, String licenseFilePath) { diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 6ab2d6d9..a1bd7361 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1489,6 +1489,7 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar Date: Mon, 5 Sep 2022 14:03:37 +0200 Subject: [PATCH 007/139] ORT Reader Implementation (#123) * [WIP] first functionality of OrtReader implemented (can create inventories) * added id resolution * added documentation and cleaned code * added ort reader tests and fixed example json Co-authored-by: Oliver Hecker <8004361+ohecker@users.noreply.github.com> --- README.md | 1 + .../tools/solicitor/reader/ort/OrtReader.java | 108 +++++++++++++++++ .../solicitor/reader/ort/OrtReaderTests.java | 92 +++++++++++++++ core/src/test/resources/analyzer-result.json | 110 ++++++++++++++++++ documentation/files/analyzer-result.json | 110 ++++++++++++++++++ documentation/master-solicitor.asciidoc | 53 ++++++++- 6 files changed, 470 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/reader/ort/OrtReaderTests.java create mode 100644 core/src/test/resources/analyzer-result.json create mode 100644 documentation/files/analyzer-result.json diff --git a/README.md b/README.md index 5ec054f4..d85b6254 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ for the project. Text or spreadsheet (Excel) based reports might be created to d * Yarn * Pip * CSV (e.g. for manual entry of data) + * OSS Review Toolkit - Analyzer (ORT) * Rules processing (using Drools Rule Engine) controls the the different phases: * Normalizing / Enhancing of license information * Handling of multilicensing (including selection of applicable licenses) and re-licensing diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java new file mode 100644 index 00000000..45aea82a --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java @@ -0,0 +1,108 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.devonfw.tools.solicitor.reader.ort; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; +import com.devonfw.tools.solicitor.reader.AbstractReader; +import com.devonfw.tools.solicitor.reader.Reader; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * A {@link Reader} which reads data generated by the ORT-Analyzer component. + */ +@Component +public class OrtReader extends AbstractReader implements Reader { + + /** + * The supported type of this {@link Reader}. + */ + public static final String SUPPORTED_TYPE = "ort"; + + /** {@inheritDoc} */ + @Override + public Set getSupportedTypes() { + + return Collections.singleton(SUPPORTED_TYPE); + } + + /** {@inheritDoc} */ + @SuppressWarnings("rawtypes") + @Override + public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, + String repoType, Map configuration) { + + int componentCount = 0; + int licenseCount = 0; + + // According to tutorial https://github.com/FasterXML/jackson-databind/ + ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + try { + Map l = mapper.readValue(this.inputStreamFactory.createInputStreamFor(sourceUrl), Map.class); + Map analyzer = (Map) l.get("analyzer"); + Map result = (Map) analyzer.get("result"); + List packages = (List) result.get("packages"); + + for (int i = 0; i < packages.size(); i++) { + Map iterator = (Map) packages.get(i); + Map singlePackage = (Map) iterator.get("package"); + String id = (String) singlePackage.get("id"); + Map vcsProcessed = (Map) singlePackage.get("vcs_processed"); + String repo = (String) vcsProcessed.get("url"); + String pURL = (String) singlePackage.get("purl"); + + String homePage = (String) singlePackage.get("homepage_url"); + if (homePage == null || homePage.isEmpty()) { + homePage = repo; + } + + ApplicationComponent appComponent = getModelFactory().newApplicationComponent(); + appComponent.setApplication(application); + componentCount++; + + // resolve id into groupId/artifactId/version/repoType + String[] resolvedId = id.split(":"); + String trueRepoType = resolvedId[0]; + String groupId = resolvedId[1]; + String artifactId = resolvedId[2]; + String version = resolvedId[3]; + + appComponent.setGroupId(groupId); + appComponent.setArtifactId(artifactId); + appComponent.setVersion(version); + appComponent.setUsagePattern(usagePattern); + appComponent.setOssHomepage(homePage); + appComponent.setRepoType(trueRepoType); + appComponent.setPackageUrl(pURL); + + // manage multiple declared licenses + List lic = (List) singlePackage.get("declared_licenses"); + if (lic.isEmpty()) { + // add empty raw license if no license info attached + addRawLicense(appComponent, null, null, sourceUrl); + } else { + for (Object cl : lic) { + licenseCount++; + addRawLicense(appComponent, cl.toString(), null, sourceUrl); + } + } + doLogging(sourceUrl, application, componentCount, licenseCount); + } + } catch (IOException e) { + throw new SolicitorRuntimeException("Could not read ort license inventory source '" + sourceUrl + "'", e); + } + } +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/ort/OrtReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/ort/OrtReaderTests.java new file mode 100644 index 00000000..29179aab --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/ort/OrtReaderTests.java @@ -0,0 +1,92 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.devonfw.tools.solicitor.reader.ort; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.tools.solicitor.common.FileInputStreamFactory; +import com.devonfw.tools.solicitor.model.ModelFactory; +import com.devonfw.tools.solicitor.model.impl.ModelFactoryImpl; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; + +public class OrtReaderTests { + private static final Logger LOG = LoggerFactory.getLogger(OrtReaderTests.class); + + Application application; + + public OrtReaderTests() { + + ModelFactory modelFactory = new ModelFactoryImpl(); + + this.application = modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com", "Python"); + OrtReader pr = new OrtReader(); + pr.setModelFactory(modelFactory); + pr.setInputStreamFactory(new FileInputStreamFactory()); + pr.readInventory("ort", "src/test/resources/analyzer-result.json", this.application, UsagePattern.DYNAMIC_LINKING, "ort", + null); + + } + + @Test + public void findArtifact() { + + List lapc = this.application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("testArtifactId") && // + ap.getVersion().equals("testVersion")) { + found = true; + assertEquals("pkg:maven/testGroupId/testArtifactId@testVersion", ap.getPackageUrl()); + break; + } + } + assertTrue(found); + } + + @Test + public void readFileAndCheckSize() { + + LOG.info(this.application.toString()); + assertEquals(1, this.application.getApplicationComponents().size()); + } + + @Test + public void testFindLicenseIfSingle() { + + List lapc = this.application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("testArtifactId") && ap.getRawLicenses().get(0).getDeclaredLicense().equals("Apache License, Version 2.0")) { + found = true; + break; + } + } + assertTrue(found); + } + + @Test + public void testHomepageWhichIsGiven() { + + List lapc = this.application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("testArtifactId") && ap.getOssHomepage().equals("https://test.com/test")) { + found = true; + break; + } + } + assertTrue(found); + } + +} diff --git a/core/src/test/resources/analyzer-result.json b/core/src/test/resources/analyzer-result.json new file mode 100644 index 00000000..22a7f571 --- /dev/null +++ b/core/src/test/resources/analyzer-result.json @@ -0,0 +1,110 @@ +{ + "repository" : { + "vcs" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "vcs_processed" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "config" : { } + }, + "analyzer" : { + "start_time" : "testStartTime", + "end_time" : "testEndTime", + "environment" : { + "ort_version" : "23ca00df86", + "java_version" : "11.0.15", + "os" : "Linux", + "processors" : 4, + "max_memory" : 1631584256, + "variables" : { + "JAVA_HOME" : "/opt/java/openjdk", + "ANDROID_HOME" : "/opt/android-sdk", + "GOPATH" : "/tmp/go" + }, + "tool_versions" : { + "NPM" : "8.5.0" + } + }, + "config" : { + "allow_dynamic_versions" : false + }, + "result" : { + "projects" : [ { + "id" : "Maven:groupId:artifactId:version", + "definition_file_path" : "", + "declared_licenses" : [ ], + "declared_licenses_processed" : { }, + "vcs" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "vcs_processed" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "homepage_url" : "", + "scope_names" : [ "compile" ] + }], + "packages" : [ { + "package" : { + "id" : "Maven:testGroupId:testArtifactId:testVersion", + "purl" : "pkg:maven/testGroupId/testArtifactId@testVersion", + "authors" : [ "testAuthor" ], + "declared_licenses" : [ "Apache License, Version 2.0" ], + "declared_licenses_processed" : { + "spdx_expression" : "Apache-2.0", + "mapped" : { + "Apache License, Version 2.0" : "Apache-2.0" + } + }, + "description" : "testDescription", + "homepage_url" : "https://test.com/test", + "binary_artifact" : { + "url" : "https://repo.maven.apache.org/maven2/testGroupId/testArtifactId/testVersion/testArtifactId-testVersion.jar", + "hash" : { + "value" : "testHash", + "algorithm" : "SHA-1" + } + }, + "source_artifact" : { + "url" : "https://repo.maven.apache.org/maven2/testGroupId/testArtifactId/testVersion/testArtifactId-testVersion-sources.jar", + "hash" : { + "value" : "testHash2", + "algorithm" : "SHA-1" + } + }, + "vcs" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "vcs_processed" : { + "type" : "Git", + "url" : "https://github.com/testproject.git", + "revision" : "", + "path" : "testproject" + } + }, + "curations" : [ ] + } ], + "issues" : {}, + "dependency_graphs" : {}, + "has_issues" : true + } + }, + "scanner" : null, + "advisor" : null, + "evaluator" : null +} \ No newline at end of file diff --git a/documentation/files/analyzer-result.json b/documentation/files/analyzer-result.json new file mode 100644 index 00000000..22a7f571 --- /dev/null +++ b/documentation/files/analyzer-result.json @@ -0,0 +1,110 @@ +{ + "repository" : { + "vcs" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "vcs_processed" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "config" : { } + }, + "analyzer" : { + "start_time" : "testStartTime", + "end_time" : "testEndTime", + "environment" : { + "ort_version" : "23ca00df86", + "java_version" : "11.0.15", + "os" : "Linux", + "processors" : 4, + "max_memory" : 1631584256, + "variables" : { + "JAVA_HOME" : "/opt/java/openjdk", + "ANDROID_HOME" : "/opt/android-sdk", + "GOPATH" : "/tmp/go" + }, + "tool_versions" : { + "NPM" : "8.5.0" + } + }, + "config" : { + "allow_dynamic_versions" : false + }, + "result" : { + "projects" : [ { + "id" : "Maven:groupId:artifactId:version", + "definition_file_path" : "", + "declared_licenses" : [ ], + "declared_licenses_processed" : { }, + "vcs" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "vcs_processed" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "homepage_url" : "", + "scope_names" : [ "compile" ] + }], + "packages" : [ { + "package" : { + "id" : "Maven:testGroupId:testArtifactId:testVersion", + "purl" : "pkg:maven/testGroupId/testArtifactId@testVersion", + "authors" : [ "testAuthor" ], + "declared_licenses" : [ "Apache License, Version 2.0" ], + "declared_licenses_processed" : { + "spdx_expression" : "Apache-2.0", + "mapped" : { + "Apache License, Version 2.0" : "Apache-2.0" + } + }, + "description" : "testDescription", + "homepage_url" : "https://test.com/test", + "binary_artifact" : { + "url" : "https://repo.maven.apache.org/maven2/testGroupId/testArtifactId/testVersion/testArtifactId-testVersion.jar", + "hash" : { + "value" : "testHash", + "algorithm" : "SHA-1" + } + }, + "source_artifact" : { + "url" : "https://repo.maven.apache.org/maven2/testGroupId/testArtifactId/testVersion/testArtifactId-testVersion-sources.jar", + "hash" : { + "value" : "testHash2", + "algorithm" : "SHA-1" + } + }, + "vcs" : { + "type" : "", + "url" : "", + "revision" : "", + "path" : "" + }, + "vcs_processed" : { + "type" : "Git", + "url" : "https://github.com/testproject.git", + "revision" : "", + "path" : "testproject" + } + }, + "curations" : [ ] + } ], + "issues" : {}, + "dependency_graphs" : {}, + "has_issues" : true + } + }, + "scanner" : null, + "advisor" : null, + "evaluator" : null +} \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index a1bd7361..1ff67838 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -344,7 +344,7 @@ Within this section the different applications (=deliverables) of the engagement <6> the type of reader; for possible values see <> <7> location of the source file to read (ResourceLoader-URL) <8> usage pattern; possible values: DYNAMIC_LINKING, STATIC_LINKING, STANDALONE_PRODUCT -<9> repoType: Repository to download the sources from: currently possible values: maven, npm; if omitted then "maven" will be taken as default +<9> repoType: Repository to download the sources from: currently possible values: maven, npm, pip, yarn, ort; if omitted then "maven" will be taken as default <10> _placeholder patterns might be used here_ @@ -601,7 +601,7 @@ These configurations may also be used to overwrite options of a https://commons. Important: In case that a component has multiple licenses attached, there needs to be a separate line in the csv file for each license. -WARNING: The CSV reader currently does not fill the attribute `packageUrl`. Any functionality/reporting based on this attribute will be disfunctional for data read bythe CSV reader. +WARNING: The CSV reader currently does not fill the attribute `packageUrl`. Any functionality/reporting based on this attribute will be disfunctional for data read by the CSV reader. === NPM @@ -683,7 +683,7 @@ In _Solicitor_ the data is read with the following part of the config === Yarn -To generate the input file required for Solicitor yarn needs to be executed with the following command within the directory that contains the project's package.json (we require JSON output here): +To generate the input file required for Solicitor, yarn needs to be executed with the following command within the directory that contains the project's package.json (we require JSON output here): ---- yarn licenses list --json > /path/to/yarnlicenses.json @@ -712,7 +712,7 @@ In _Solicitor_ the data is read with the following part of the config === Pip -To generate the input file required for Solicitor one has follow two steps: +To generate the input file required for Solicitor, one has to follow two steps: * Capsulate software with all relevant dependencies/requirements in a virtual environment (venv) * Install the pip-licenses plugin within this virtual environment @@ -743,6 +743,50 @@ In _Solicitor_ the data is read with the following part of the config } ] ---- +=== OSS Review Toolkit (ORT) + +In order to use the https://github.com/oss-review-toolkit/ort#analyzer[analyzer library of ORT], one must first install the software and run it to generate the result file. +The detailed way on installing ORT can be found https://github.com/oss-review-toolkit/ort#installation[here] and a tutorial on how to run the analyzer library can be found https://github.com/oss-review-toolkit/ort/blob/main/docs/getting-started.md#4-run-the-analyzer-on-mime-types[here]. + +Usually, the command to run the analyzer and get extract the result file from a project looks like this: +---- +docker run -v C:\\path\\to\\project/:/project ort --info analyze -f JSON -i /project -o /project/ort/analyzer +---- + +Note that this command only works for the installation via https://www.docker.com/[Docker] and that we require JSON as the output format. For other installation methods, you need to adjust the command accordingly. + +It might also be necessary to set up a customized configuration for the analyzer. This can be achieved through a configuration file. The default path for that is the .ort/config/ directory below the current user's home directory. We can place a ort.conf file there, in which we can declare various configurations e.g. allowing dynamic versions in npm components via +---- +analyzer { + allowDynamicVersions = true +} +---- + +Further information about the configuration file can be found https://github.com/oss-review-toolkit/ort#configuration[here]. + +The result file should look like the following + +[source] +---- +include::files/analyzer-result.json[] + +---- + +Source: https://github.com/oss-review-toolkit/ort#analyzer + +In _Solicitor_ the data is read with the following part of the config + +---- +"readers" : [ { + "type" : "ort", + "source" : "file:path/to/analyzer-result.json", + "usagePattern" : "DYNAMIC_LINKING", + "repoType" : "ort" +} ] +---- + +WARNING: The ORT reader currently does not yet fill the attribute `licenseUrl`. Any functionality/reporting based on this attribute will be disfunctional for data read by the ORT reader. + === Gradle (Windows) For the export of the licenses from a Gradle based project the Gradle License Plugin is used. @@ -1489,6 +1533,7 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar >. * Added support for API changes of new scancode release (v31) https://github.com/nexB/scancode-toolkit/releases/tag/v31.0.1. * https://github.com/devonfw/solicitor/issues/124: Added documentation of '--production' option for npm-license-checker plugin. * https://github.com/devonfw/solicitor/issues/125: Deprecated usage of npm-license-crawler. From e0fc84c567ceca875ccb2eaab951dff9571a6612 Mon Sep 17 00:00:00 2001 From: malkam <108339027+mahmoudAlkam@users.noreply.github.com> Date: Thu, 27 Oct 2022 17:11:18 +0200 Subject: [PATCH 008/139] Update master-solicitor.asciidoc (#132) Adapt NPM reader (Static Linking instead of Dynamic Linking ) --- documentation/master-solicitor.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 1ff67838..056a44cb 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -638,7 +638,7 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "npm-license-checker", "source" : "file:path/to/licenses.json", - "usagePattern" : "DYNAMIC_LINKING", + "usagePattern" : "STATIC_LINKING", "repoType" : "npm" } ] ---- @@ -676,7 +676,7 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "npm-license-crawler-csv", "source" : "file:path/to/licenses.csv", - "usagePattern" : "DYNAMIC_LINKING", + "usagePattern" : "STATIC_LINKING", "repoType" : "npm" } ] ---- From 3d3fa1274a1cb9026e3885f5fbe3ff8b9fb23ccd Mon Sep 17 00:00:00 2001 From: pgarus <35223024+pgarus97@users.noreply.github.com> Date: Thu, 27 Oct 2022 17:13:08 +0200 Subject: [PATCH 009/139] Fixed bug in which guessedLicenseUrl and guessedLicenseUrlAuditInfo were not filled correctly in aggregated inventory (#133) --- .../sql/normalizedlicenses_aggregated_applications.sql | 6 ++++++ documentation/master-solicitor.asciidoc | 1 + 2 files changed, 7 insertions(+) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql index 8f77e968..8f6a0da8 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql @@ -27,6 +27,8 @@ select "effectiveNormalizedLicenseType", "effectiveNormalizedLicense", "effectiveNormalizedLicenseUrl", + "guessedLicenseUrl", + "guessedLicenseUrlAuditInfo", "legalPreApproved", "copyLeft", "licenseCompliance", @@ -58,6 +60,8 @@ from ( l."effectiveNormalizedLicenseType", l."effectiveNormalizedLicense", l."effectiveNormalizedLicenseUrl", + l."guessedLicenseUrl", + l."guessedLicenseUrlAuditInfo", l."legalPreApproved", l."copyLeft", l."licenseCompliance", @@ -91,6 +95,8 @@ from ( "effectiveNormalizedLicenseType", "effectiveNormalizedLicense", "effectiveNormalizedLicenseUrl", + "guessedLicenseUrl", + "guessedLicenseUrlAuditInfo", "legalPreApproved", "copyLeft", "licenseCompliance", diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 056a44cb..a9c1fe07 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1533,6 +1533,7 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar >. * Added support for API changes of new scancode release (v31) https://github.com/nexB/scancode-toolkit/releases/tag/v31.0.1. * https://github.com/devonfw/solicitor/issues/124: Added documentation of '--production' option for npm-license-checker plugin. From d420d81a5832c6002cc42e50e512106dcbbd11e8 Mon Sep 17 00:00:00 2001 From: pgarus <35223024+pgarus97@users.noreply.github.com> Date: Thu, 27 Oct 2022 17:19:02 +0200 Subject: [PATCH 010/139] Spellcheck Extension: Support for run-together word formats like camel case (Issue #129) (#134) * added camelcase support in spellcheck and fixed first batch of spelling errors * increased run together words limit to 4 * decreased minimal word length to 2 * added project specific words to dictionary file * increased run-together word limit to 5, added changelog and final dictionary fix Co-authored-by: Oliver Hecker <8004361+ohecker@users.noreply.github.com> --- .spellcheck.yml | 3 ++ documentation/master-solicitor.asciidoc | 69 +++++++++++++------------ solicitor.dict | 39 +++++++++++--- 3 files changed, 71 insertions(+), 40 deletions(-) diff --git a/.spellcheck.yml b/.spellcheck.yml index 4b283463..a7a60359 100644 --- a/.spellcheck.yml +++ b/.spellcheck.yml @@ -3,6 +3,9 @@ matrix: aspell: lang: en d: en_US + run-together: true + run-together-limit: 5 + run-together-min: 2 sources: - documentation/*.asciidoc dictionary: diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index a9c1fe07..51072022 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -11,7 +11,7 @@ SPDX-License-Identifier: Apache-2.0 == Introduction -Todays software projects often make use of large amounts of Open Source software. Being +Today's software projects often make use of large amounts of Open Source software. Being compliant with the license obligations of the used software components is a prerequisite for every such project. This results in different requirements that the project might need to fulfill. Those requirements can be grouped into two main categories: * Things that need to be done to actually fulfill license obligations @@ -26,14 +26,14 @@ While working on these easy looking tasks, they might get complex due to various * The number of open source components might be quite large (>> 100 for a typical webapplication based on state of the art programming frameworks) * Agile development and rapid changes of used components result in frequent changes of the inventory -* Open Source usage scenarios and license obligations might be OK in one context (e.g. in the relation between a software developer and his client) but might be completely inacceptable in another context (e.g. when the client distributes the same software to end customers) -* Legal interpretation of license conditions often differ from organisation to organisation and result in different compliance rules to be respected. +* Open Source usage scenarios and license obligations might be OK in one context (e.g. in the relation between a software developer and his client) but might be completely unacceptable in another context (e.g. when the client distributes the same software to end customers) +* Legal interpretation of license conditions often differ from organization to organization and result in different compliance rules to be respected. * License information for components is often not available in a standardized form which would allow automatic processing * Tools for supporting the license management processes are often specific to a technology or build tool and do not support all aspects of OSS license management. Of course there are specific commercial tool suites which address the IP rights and license domain. But due to high complexity and license costs those tools are out of reach for most projects - at least for permanent use. -_Solicitor_ tries to address some of the issues hightlighted above. In its initial version it is a tool for programmatically executing a process which was originally defined as an Excel-supported manual process. +_Solicitor_ tries to address some of the issues highlighted above. In its initial version it is a tool for programmatically executing a process which was originally defined as an Excel-supported manual process. When running _Solicitor_ three subsequent processing steps are executed: @@ -67,7 +67,7 @@ In the _normalization_ step the license information is completed and unified. Information not contained in the raw data is added. Where possible the applicable licenses are expressed by https://spdx.org/[SPDX-IDs]. -Many open source compontents are available via multi licensing models. +Many open source components are available via multi licensing models. Within _qualification_ the finally applicable licenses are selected. In the _legal assessment_ the compliance of applicable licenses will be checked based on generic rules defined in company wide policies and possibly project specific project specific extensions. @@ -82,7 +82,7 @@ image:solution.png[width=100%,scaledwidth=100%] There are three major technical components: The _reader_ and _writer_ components are performing import and export of data. The business logic - doing _normalization_, _qualification_ and _legal assessment_ is done by a _rule engine_. Rules are mainly defined via _decision tables_. _Solicitor_ comes with a starting set of rules for _normalization_ and _qualification_ but these rulesets need to be extended within the projects. Rules for legal evaluation need to be completely defined by the user. -_Solicitor_ is working without additional persisted data: When being executed it generates the output direcly from the read input data after processing the business rules. +_Solicitor_ is working without additional persisted data: When being executed it generates the output directly from the read input data after processing the business rules. === Data Model @@ -121,7 +121,7 @@ The internal business data model consists of 6 entities: | engagementType | EngagementType | the engagement type; possible values: INTERN, EXTERN | clientName | String | name of the client | goToMarketModel | GoToMarketModel | the go-to-market-model; possible values: LICENSE -| contractAllowsOss | boolean | does the contract explicitely allow OSS? +| contractAllowsOss | boolean | does the contract explicitly allow OSS? | ossPolicyFollowed | boolean | is the companies OSS policy followed? | customerProvidesOss | boolean | does the customer provide the OSS? |=== @@ -242,13 +242,13 @@ To get an overview of the available command line options use [listing] java -jar solicitor.jar -h -.Adressing of resources +.Addressing of resources **** -For unique adressing of resources *to be read* (configuration files, input data, rule templates and decision tables) _Solicitor_ makes use of the Spring ResourceLoader functionality, see https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#resources-resourceloader . This allows to load from the classpath, the filesystem or even via http get. +For unique addressing of resources *to be read* (configuration files, input data, rule templates and decision tables) _Solicitor_ makes use of the Spring ResourceLoader functionality, see https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#resources-resourceloader . This allows to load from the classpath, the filesystem or even via http get. If you want to reference a file in the filesystem you need to write it as follows: `file:path/to/file.txt` -Note that this only applies to resources being read. Output files are adressed without that prefix. +Note that this only applies to resources being read. Output files are addressed without that prefix. **** === Project Configuration File @@ -286,7 +286,7 @@ should be given relative to the location of the configuration file this might be using the special placeholder `${cfgdir}` as described in the following. ==== Placeholders within the configuration file -Within certain parts of the configuration file (path and filenames) special placeholders might be used to parameterize the configuration. These areas are explicitely marked in the following +Within certain parts of the configuration file (path and filenames) special placeholders might be used to parameterize the configuration. These areas are explicitly marked in the following description. These placeholders are available: @@ -315,12 +315,12 @@ The leading section of the config file defines some metadata and the engagement <4> the engagement type; possible values: INTERN, EXTERN <5> name of the client (any string) <6> the go-to-market-model; possible values: LICENSE -<7> does the contract explicitely allow OSS? (boolean) +<7> does the contract explicitly allow OSS? (boolean) <8> is the companies OSS policy followed? (boolean) <9> does the customer provide the OSS? (boolean) ==== Applications -Within this section the different applications (=deliverables) of the engagement are defined. Furtheron for each application at least one reader needs to be defined which imports the component and license information. +Within this section the different applications (=deliverables) of the engagement are defined. Furthermore, for each application at least one reader needs to be defined which imports the component and license information. [listing] "applications" : [ { @@ -375,7 +375,7 @@ They are defined as a sequence of rule templates and corresponding XLS (or CSV) "ruleSource" : "classpath:samples/LegalEvaluationSample.xls", "templateSource" : "classpath:com/.../rules/rule_templates/LegalEvaluation.drt", "ruleGroup" : "LegalEvaluation", - "decription" : "final legal evaluation based on the rules defined by legal" + "description" : "final legal evaluation based on the rules defined by legal" } ], <1> type of the rule; only possible value: `dt` which stands for "decision table" @@ -383,7 +383,7 @@ They are defined as a sequence of rule templates and corresponding XLS (or CSV) data (given by `ruleSource`) does not exist; if set to `false` a missing XLS/CSV table will result in program termination <3> location of the tabular decision table data. This might either point directly to the XLS or CSV file -or only give the resource name without suffix. In this case _Solictor_ will dynamically test for existing +or only give the resource name without suffix. In this case _Solicitor_ will dynamically test for existing resources by appending suffixes _xls_ and _csv_. <4> location of the drools rule template to be used to define the rules together with the decision table data <5> id of the group of rules; used to reference it e.g. when doing logging @@ -449,7 +449,7 @@ To simplify setting up a new project _Solicitor_ provides an option to create a java -jar solicitor.jar -wiz some/directory/path Besides the necessary configuration file this includes also empty XLS or CSV files for defining project -specific rules which amend the builtin rules. Furtheron a sample `license.xml` file is provided to +specific rules which amend the builtin rules. Furthermore, a sample `license.xml` file is provided to directly enable execution of solicitor and check functionality. This configuration then serves as starting point for project specific configuration. @@ -917,7 +917,7 @@ is defined by the sequence of declaration in the config file. Processing of the be finished when there are no more rules to fire in that group. Processing of the next group will then start. Rule groups which have been finished processing will not be resumed even if rules within that group might have been activated again due to changes of the facts. === Extended comparison syntax -By default any condtions given in the fields of decision tables are simple textual comparisons: The condition +By default any conditions given in the fields of decision tables are simple textual comparisons: The condition is true if the property of the model is identical to the given value in the XLS (or CSV) sheet. Depending on the configuration of the rule templates for some fields, an extended syntax might be available. For those fields the following syntax applies: @@ -940,8 +940,8 @@ In this phase the license data imported via the readers is cleaned and normalize The phase itself consists of two decision tables / rule groups: -==== Decision Table: Explicitely setting Licenses -With this decision table is is possible to explicitely assign NormalizedLicenses to components. This will be used if the imported RawLicense data is either incomplete or incorrect. Items which have been processed by rules of this group will not be reprocessed by the next rule group. +==== Decision Table: Explicitly setting Licenses +With this decision table is is possible to explicitly assign NormalizedLicenses to components. This will be used if the imported RawLicense data is either incomplete or incorrect. Items which have been processed by rules of this group will not be reprocessed by the next rule group. Decision table data: `LicenseAssignmentV2*.xls/csv` @@ -950,7 +950,7 @@ Decision table data: `LicenseAssignmentV2*.xls/csv` ** `Engagement.engagementName` ** `Application.applicationName` ** `ApplicationComponent.groupId` icon:magic[] -** `ApplicationCompomnent.artifactId` icon:magic[] +** `ApplicationComponent.artifactId` icon:magic[] ** `ApplicationComponent.version` icon:magic[] ** `RawLicense.origin` icon:magic[] (new with "V2" version of rules) ** `RawLicense.declaredLicense` icon:magic[] @@ -993,7 +993,7 @@ Within this phase the actually applicable licenses will be selected for each com This phase consists of two decision tables. ==== Choosing specific License in case of Multi-Licensing -This group of rules has the speciality that it might match to a group of NormalizedLicenses associated to an ApplicationComponent. In case that multiple licenses are associated to an ApplicationComponent one of them might be selected as "effective" license and the others might be marked as `Ignored`. +This group of rules has the specialty that it might match to a group of NormalizedLicenses associated to an ApplicationComponent. In case that multiple licenses are associated to an ApplicationComponent one of them might be selected as "effective" license and the others might be marked as `Ignored`. Decision table data: `MultiLicenseSelection*.xls/csv` @@ -1036,10 +1036,10 @@ icon:magic[]: On these fields the <> might be used === Phase 3: Legal evaluation -The third phase ist the legal evaluation of the licenses and the check, whether OSS usage is according to defined legal policies. Again this phase comprises two decision tables. +The third phase is the legal evaluation of the licenses and the check, whether OSS usage is according to defined legal policies. Again this phase comprises two decision tables. ==== Pre-Evaluation based on common rules -Within the pre evaluation the license info is checked against standard OSS usage policies. This roughly qualifies the usage and might already determine licenses which are OK in any case or which need to be further evaluated. Furtheron they qualify whether the license text or source code needs to be included in the distribution. The rules in this decision table are only based on the `effectiveNormalizedLicense` and do not consider any project, application of component information. +Within the pre evaluation the license info is checked against standard OSS usage policies. This roughly qualifies the usage and might already determine licenses which are OK in any case or which need to be further evaluated. Furthermore, they qualify whether the license text or source code needs to be included in the distribution. The rules in this decision table are only based on the `effectiveNormalizedLicense` and do not consider any project, application of component information. Decision table data: `LegalPreEvaluation*.xls/csv` @@ -1088,7 +1088,7 @@ groups to be processed in sequence. When using the builtin default base configur To use your own rule data there are three approaches: * Include your own `rules` section in the project configuration file (so not inheriting from the builtin base configuration file) and reference your own decision tables there. -* Create your own "Solicitor Extension" which might completely redefine/replace the buitin `Solicitor` setup including all decision tables and the base configuration file. See <> for details. +* Create your own "Solicitor Extension" which might completely redefine/replace the builtin `Solicitor` setup including all decision tables and the base configuration file. See <> for details. * Make use of the optional project specific decision tables which are defined in the default base configuration: For every builtin decision table there is an optional external decision table (expected in the filesystem) which will be checked for existence. If such external decision table exists it will be processed first - before processing the builtin decision table. Thus is it possible to amend / override the builtin rules by project specific rules. When you create the starter configuration of your project as described in <>, those project specific decision tables are automatically created. == Reporting and Creating output documents @@ -1127,14 +1127,14 @@ is done: * a `rowCount` column is added to the result which gives the position of the entry in the result set (starting with 1). * Columns named `ID_` are replaced with columns named `OBJ_`. The fields of those columns are filled with the corresponding original model objects (java objects). -WARNING: The result table column `OBJ_` gives access to the native Solicitor data model (java objects), e.g. in the Velocity writer. As this breaks the decoupling done via the SQL database using this feature is explicitely discouraged. It should only be used with high caution and in exceptional situations. The feature might be discontinued in future versions without prior notice. +WARNING: The result table column `OBJ_` gives access to the native Solicitor data model (java objects), e.g. in the Velocity writer. As this breaks the decoupling done via the SQL database using this feature is explicitly discouraged. It should only be used with high caution and in exceptional situations. The feature might be discontinued in future versions without prior notice. === Determining difference to previously stored model When using the command line option `-d` _Solicitor_ can determine difference information between two different data models (e.g. the difference between the licenses of the current release and a former release.) The difference is calculated on the result of the above described SQL statements: * First the internal reporting database is created for the current data model and all defined SQL statements are executed -* Then the internal database is recreated for the "old" data model and all defined SQL stements are executed again +* Then the internal database is recreated for the "old" data model and all defined SQL statements are executed again * Finally for each defined result table the difference between the current result and the "old" result is calculated @@ -1148,7 +1148,7 @@ The correlation algorithm will first try to match rows using `CORR_KEY_0`. It wi * there are no unmatched "new" rows OR * there are no unmatched "old" rows -The result of the correlation / difference calulation is stored in the reporting table data structure. For each row the status is accessible if +The result of the correlation / difference calculation is stored in the reporting table data structure. For each row the status is accessible if * The row is "new" (did not exist in the old data) * The row is unchanged (no changes in the field values representing the properties of the _Solicitor_ data model) @@ -1159,7 +1159,7 @@ For each field of "changed" or "unchanged" rows the following status is availabl * Field is "changed" * Field is "unchanged" -For each field of such rows it is furtheron possible to access the new and the old field value. +For each field of such rows it is further on possible to access the new and the old field value. === Sample SQL statement @@ -1175,7 +1175,7 @@ NOTE: Above example also shows how the case sensitive column names have to be ha === Writers -The above dscribed SQL processing is identical for all Writers. Writers only differ in the +The above described SQL processing is identical for all Writers. Writers only differ in the way how the output document is created based on a template and the reporting table data obtained by the SQL transformation. @@ -1188,7 +1188,7 @@ directly put to the into Velocity Context. For further information see the * Velocity Documentation -* The _Solicitor_ JavaDoc (which also includes datails on how to access the diff information for rows and fields of reporting data tables) +* The _Solicitor_ JavaDoc (which also includes details on how to access the diff information for rows and fields of reporting data tables) * The samples included in _Solicitor_ ==== Excel Writer @@ -1206,7 +1206,7 @@ reporting data tables as defined in the Writer configuration like e.g.: Whenever such a string is found in a cell this indicates that this row is a template row. For each entry in the respective resporting data table a copy of this row is created and the attribute replacement will be done with the data from that reporting table. (The pattern `\#...#` will be removed when copying.) ===== Attribute replacement -Within each row which was copied in the previous step the templating logic searches for the string pattern `$someAttributeName$` where `someAttributeName` corresponds to the column names of the reporting table. Any such occurence is replaced with the corresponding data value. +Within each row which was copied in the previous step the templating logic searches for the string pattern `$someAttributeName$` where `someAttributeName` corresponds to the column names of the reporting table. Any such occurrence is replaced with the corresponding data value. ===== Representation of Diff Information In case that a difference processing (new vs. old model data) was done this will be represented @@ -1311,7 +1311,7 @@ The general workflow when integrating with ScanCode consists of the following 3 . Execute _Solicitor_ in a "classic" way i.e. just based on the data provided via the Readers as described in <>. Besides the normal reports/documents generated this will also create scripts for downloading the needed OSS source codes and run Scancode. -. Download source codes and run ScanCode by executing the generated scripts. The downloadad sources and ScanCode results will be saved to a directory tree in the local filesytem. +. Download source codes and run ScanCode by executing the generated scripts. The downloaded sources and ScanCode results will be saved to a directory tree in the local filesystem. . Execute _Solicitor_ a second time. For all ApplicationComponents where ScanCode information is available (stored in the local directory tree) the license data as obtained from the Readers is replaced by this information. The data model is enriched with the found copyright and notice file information. Reports (see <>) are now based on the ScanCode data (where available). @@ -1396,7 +1396,7 @@ artifacts: <5> URL pointing to license text. <6> Copyrights to set. Optional. If defined then all found copyrights will be replaced by the list of copyrights given here. <7> A single copyright. -<8> Another copyright. Note that due to YAML syntax any string containing `:` needs to be enclosed with parantheses +<8> Another copyright. Note that due to YAML syntax any string containing `:` needs to be enclosed with parentheses <9> Further packages to follow. ==== Decision table rules @@ -1487,7 +1487,7 @@ The template is part of the <> and requires S == Extending Solicitor `Solicitor` comes with a sample rule data set and sample reporting templates. In general it will be required to correct, supplement and extend this data sets and templates. This can be done straightforward -by creating copies of the appropriate resources (rule data XLS/CSV and template files), adopting them and furtheron referencing those copies instead of the original resources from the project configuration file. +by creating copies of the appropriate resources (rule data XLS/CSV and template files), adopting them and further on referencing those copies instead of the original resources from the project configuration file. Even though this approach is possible it will result in hard to maintain configurations, especially in the case of multiple projects using `Solicitor` in parallel. @@ -1533,6 +1533,7 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar >. * Added support for API changes of new scancode release (v31) https://github.com/nexB/scancode-toolkit/releases/tag/v31.0.1. diff --git a/solicitor.dict b/solicitor.dict index f209a4c5..5df7788b 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -1,9 +1,36 @@ -guessedLicenseContent -guessedLicenseUrl -guessedLicenseUrlAuditInfo -infos -licenseurls scancode -ScancodeDownloadScript SOLI vm +Capsulate +Dloader +Dpropertyname +Dsome +RHS +Unlicense +WTFPL +Zlib +Zope +cfgdir +conf +deliverables +drt +dt +ec +eug +fas +gradle +http +licenseRefUrl +licenseToIgnoreN +licenseurls +linux +natively +nd +ort +oss +sql +url +urls +velo +venv +zA \ No newline at end of file From 2ca6caece0b001e5dfeb67642181f9245191b41a Mon Sep 17 00:00:00 2001 From: duph97 Date: Tue, 1 Nov 2022 16:52:28 +0100 Subject: [PATCH 011/139] added LicenseNameMapping rules (#126) * added LicenseNameMapping rules. * deleted redundant bouncy castle rule --- .../rules/LicenseNameMappingSample.xls | Bin 77312 -> 77824 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls index e8789b0671b5e1da506e5d9bbf1209461ddfab02..ac99dcb1df9b179fe0ca5581bda71cc8ec26347f 100644 GIT binary patch delta 14014 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 delta 13023 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 RYJN>>M^zGe&fit3{|EUQAZ!2t From 652e7090d9589f9de8a6616aee2887014def663e Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 2 Nov 2022 17:16:34 +0100 Subject: [PATCH 012/139] Split core module (java code) and application module (executable jar) (#138) --- .github/workflows/release.yml | 2 +- app/pom.xml | 64 +++++++++++++++++++ core/pom.xml | 10 +-- .../tools/solicitor/SolicitorApplication.java | 57 ++++++++--------- documentation/master-solicitor.asciidoc | 3 +- pom.xml | 1 + 6 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 app/pom.xml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 77f47e88..f4a12c61 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./core/target/solicitor.jar + asset_path: ./app/target/solicitor.jar asset_name: solicitor.jar asset_content_type: application/octet-stream - name: Upload Userguide diff --git a/app/pom.xml b/app/pom.xml new file mode 100644 index 00000000..ca6e0716 --- /dev/null +++ b/app/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + + + com.devonfw.tools + solicitor-app + 1.4.0-SNAPSHOT + + Solicitor App (Main Application) + A tool for rule based checking of license conditions in software projects. + + + Apache Software License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + scm:git:file://. + scm:git:file://. + scm:git:file://. + + + + 1.8 + dd-MM-yy_HH:mm + ${maven.build.timestamp} + UTF-8 + + + + + ${project.groupId} + solicitor-core + ${project.version} + + + + + + solicitor + + + org.springframework.boot + spring-boot-maven-plugin + + com.devonfw.tools.solicitor.SolicitorApplication + ZIP + + + + + diff --git a/core/pom.xml b/core/pom.xml index 5376ce74..52a676d3 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -15,7 +15,7 @@ solicitor-core 1.4.0-SNAPSHOT - Solicitor Core (Main Application) + Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. @@ -164,7 +164,6 @@ - solicitor src/main/resources @@ -220,13 +219,6 @@ - - org.springframework.boot - spring-boot-maven-plugin - - ZIP - - org.codehaus.mojo license-maven-plugin diff --git a/core/src/main/java/com/devonfw/tools/solicitor/SolicitorApplication.java b/core/src/main/java/com/devonfw/tools/solicitor/SolicitorApplication.java index 715977fd..67415452 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/SolicitorApplication.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/SolicitorApplication.java @@ -13,39 +13,38 @@ import com.devonfw.tools.solicitor.common.LogMessages; /** - * The main class. Triggers command line processing and bootstraps the Spring - * application context. It delegates to class {@link Solicitor} for further - * processing. + * The main class. Triggers command line processing and bootstraps the Spring application context. It delegates to class + * {@link Solicitor} for further processing. */ @SpringBootApplication public class SolicitorApplication { - private static final Logger LOG = LoggerFactory.getLogger(SolicitorApplication.class); - - /** - * The standard java main method. - * - * @param args command line arguments - */ - public static void main(String[] args) { - - try { - SolicitorCliProcessor scp = new SolicitorCliProcessor(); - CommandLineOptions clo = scp.parse(args); - if (clo == null) { - System.exit(3); - } - // only continue processing if help option was not selected - if (!clo.help) { - ApplicationContext context = SpringApplication.run(SolicitorApplication.class, args); - Solicitor solicitor = context.getBean(Solicitor.class); - solicitor.run(clo, String.join(" ", args)); - } - } catch (RuntimeException e) { - LOG.error(LogMessages.ABORTED.msg(), e); - System.exit(3); - } - + private static final Logger LOG = LoggerFactory.getLogger(SolicitorApplication.class); + + /** + * The standard java main method. + * + * @param args command line arguments + */ + public static void main(String[] args) { + + try { + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + CommandLineOptions clo = scp.parse(args); + if (clo == null) { + System.exit(3); + } + // only continue processing if help option was not selected + if (!clo.help) { + ApplicationContext context = SpringApplication.run(SolicitorApplication.class, args); + Solicitor solicitor = context.getBean(Solicitor.class); + solicitor.run(clo, String.join(" ", args)); + } + } catch (RuntimeException e) { + LOG.error(LogMessages.ABORTED.msg(), e); + System.exit(3); } + } + } diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 51072022..07d8b092 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1533,7 +1533,8 @@ java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar >. * Added support for API changes of new scancode release (v31) https://github.com/nexB/scancode-toolkit/releases/tag/v31.0.1. diff --git a/pom.xml b/pom.xml index 383b5186..0264f875 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ documentation core + app logo From 6053c1ea921bcbd5479ecaf3edbe7bdef27add3d Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 4 Nov 2022 17:52:50 +0100 Subject: [PATCH 013/139] Refactor scancode interface to provide API to access alternative sources for deep license scan information (#140) * Restructuring/Generalizing Scancode integration * Allow multiple ComponentInfoAdapter beans to be defined / executed. * Update documentation --- .../tools/solicitor/common/LogMessages.java | 2 +- .../componentinfo/ComponentInfo.java | 40 ++++ .../componentinfo/ComponentInfoAdapter.java | 30 +++ .../ComponentInfoAdapterException.java | 38 ++++ .../ComponentInfoInventoryProcessor.java | 166 +++++++++++++++ .../solicitor/componentinfo/LicenseInfo.java | 19 ++ .../scancode/ScancodeComponentInfo.java} | 132 ++++++++++-- .../scancode/ScancodeFileAdapter.java | 85 +++++--- .../solicitor/scancode/ScancodeAdapter.java | 21 -- .../solicitor/scancode/ScancodeException.java | 36 ---- .../scancode/ScancodeInventoryProcessor.java | 193 ------------------ documentation/master-solicitor.asciidoc | 20 ++ 12 files changed, 486 insertions(+), 296 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapter.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapterException.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java rename core/src/main/java/com/devonfw/tools/solicitor/{scancode/ComponentScancodeInfos.java => componentinfo/scancode/ScancodeComponentInfo.java} (59%) rename core/src/main/java/com/devonfw/tools/solicitor/{ => componentinfo}/scancode/ScancodeFileAdapter.java (75%) delete mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java delete mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java delete mode 100644 core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 0269cde4..8204c9f9 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -81,7 +81,7 @@ public enum LogMessages { "Experimental feature ACTIVE: Start enriching the inventory data with Scancode data (as far as available)"), // SCANCODE_FEATURE_DEACTIVATED(55, "The experimental feature for enriching the inventory with scancode data is DEACTIVATED"), // - SCANCODE_INFO_READ(56, "Scancode information was read for {} out of {} ApplicationComponents"), // + COMPONENT_INFO_READ(56, "External component information was read for {} out of {} ApplicationComponents"), // CURATIONS_NOT_EXISTING(57, "Curations file '{}' not found. No curations will be applied."), // CURATIONS_PROCESSING(58, "Curations file '{}' exists. Applying curations."); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java new file mode 100644 index 00000000..486744ef --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java @@ -0,0 +1,40 @@ +package com.devonfw.tools.solicitor.componentinfo; + +import java.util.Collection; + +/** + * Data structure which holds information about a component which comes from an external data source, like the results + * of a scancode scan. + * + */ +public interface ComponentInfo { + + /** + * Gets all copyrights. + * + * @return the copyrights + */ + Collection getCopyrights(); + + /** + * Gets all licenses. + * + * @return all licenses + */ + Collection getLicenses(); + + /** + * Gets the path to the notice file (if any) + * + * @return path to the notice file + */ + String getNoticeFilePath(); + + /** + * Gets the url to the license text + * + * @return url to the license text + */ + String getUrl(); + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapter.java new file mode 100644 index 00000000..c984b044 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapter.java @@ -0,0 +1,30 @@ +package com.devonfw.tools.solicitor.componentinfo; + +import org.springframework.core.annotation.Order; + +/** + * Adapter for reading {@link ComponentInfo} data for a package. There might be different beans in the application + * implementing this interface. Processing will the be done based on the given {@link Order}. The first non null results + * will be taken in this case. + */ +public interface ComponentInfoAdapter { + + /** + * Processing order to be used for the ComponentInfoAdapter. Use in {@link Order} annotation of the implementing + * spring bean. You might do arithmetics based on this to create higher or lower priority adapters. (higher priority = + * lower number !) + */ + public static final int DEFAULT_PRIO = 200; + + /** + * Retrieves the component information for a package identified by the given package URL. Returns the data as a + * {@link ComponentInfo} object. + * + * @param packageUrl The identifier of the package for which information is requested + * @return the data for the component. null is returned if no data is available, + * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no + * data available no exception will be thrown. Instead null will be returned in such a case. + */ + ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException; + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapterException.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapterException.java new file mode 100644 index 00000000..e1e91a4a --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapterException.java @@ -0,0 +1,38 @@ +package com.devonfw.tools.solicitor.componentinfo; + +/** + * Exception indicating that the {@link ComponentInfoAdapter} had a problem reading data. This should not be used if + * there is simply no data available for the given component but only if there was either a general problem reading data + * or with reading the (existing) specific data for the component. + */ +public class ComponentInfoAdapterException extends Exception { + + /** + * A constructor. + */ + public ComponentInfoAdapterException() { + + } + + /** + * A constructor. + * + * @param message the message of the exception + */ + public ComponentInfoAdapterException(String message) { + + super(message); + } + + /** + * A constructor. + * + * @param message the message of the exception + * @param cause the underlying {@link Throwable}. + */ + public ComponentInfoAdapterException(String message, Throwable cause) { + + super(message, cause); + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java new file mode 100644 index 00000000..ae50de19 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -0,0 +1,166 @@ +package com.devonfw.tools.solicitor.componentinfo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.InventoryProcessor; +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.model.ModelFactory; +import com.devonfw.tools.solicitor.model.ModelRoot; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.inventory.RawLicense; +import com.devonfw.tools.solicitor.model.masterdata.Application; + +/** + * An {@link InventoryProcessor} which looks up license information for the found application components / packages at + * some external data source , like a scancode file store. If license information is found then the license, copyright + * and notice file information of the model will be replaced by the data obtained from this source. + * + */ +@Component +@Order(InventoryProcessor.BEFORE_RULE_ENGINE) +public class ComponentInfoInventoryProcessor implements InventoryProcessor { + + private static class Statistics { + public int componentsTotal; + + public int componentsWithComponentInfo; + + public void add(Statistics statistics) { + + this.componentsTotal += statistics.componentsTotal; + this.componentsWithComponentInfo += statistics.componentsWithComponentInfo; + } + } + + /** + * Origin data for raw license objects created by this Class. Due to compatibility reasons this is named "scancode" + * even due to the fact that it might originate from other sources. + */ + private static final String ORIGIN_COMPONENTINFO = "scancode"; + + private static final Logger LOG = LoggerFactory.getLogger(ComponentInfoInventoryProcessor.class); + + private ComponentInfoAdapter[] componentInfoAdapters; + + private ModelFactory modelFactory; + + /** + * The constructor. + */ + public ComponentInfoInventoryProcessor() { + + } + + @Override + public void processInventory(ModelRoot modelRoot) { + + Statistics overall = new Statistics(); + for (Application application : modelRoot.getEngagement().getApplications()) { + for (ApplicationComponent ac : application.getApplicationComponents()) { + Statistics single = processApplicationComponent(ac); + overall.add(single); + } + } + LOG.info(LogMessages.COMPONENT_INFO_READ.msg(), overall.componentsWithComponentInfo, overall.componentsTotal); + } + + /** + * Process a single {@link ApplicationComponent}. + * + * @param ac application component + * @return processing statistics + */ + private Statistics processApplicationComponent(ApplicationComponent ac) { + + Statistics statistics = new Statistics(); + statistics.componentsTotal = 1; + + if (ac.getPackageUrl() != null) { + ComponentInfo componentInfo = null; + try { + for (ComponentInfoAdapter cia : this.componentInfoAdapters) { + componentInfo = cia.getComponentInfo(ac.getPackageUrl()); + // stop querying further adapters if some info was returned + if (componentInfo != null) { + break; + } + } + } catch (ComponentInfoAdapterException e) { + throw new SolicitorRuntimeException("Exception when reading component info data source", e); + } + if (componentInfo != null) { + statistics.componentsWithComponentInfo = 1; + ac.removeAllRawLicenses(); + + if (componentInfo.getNoticeFilePath() != null) { + ac.setNoticeFileUrl(componentInfo.getNoticeFilePath()); + } + + for (LicenseInfo li : componentInfo.getLicenses()) { + addRawLicense(ac, li.getSpdxid(), li.getLicenseFilePath(), ORIGIN_COMPONENTINFO); + } + String copyrights = String.join("\n", componentInfo.getCopyrights()); + ac.setCopyrights(copyrights); + // check whether VendorUrl is included in input file or not + if (componentInfo.getUrl() != null) { + ac.setOssHomepage(componentInfo.getUrl()); + } + } else { + // no ComponentInfos info found for ac + } + } else { + // can this happen? + } + return statistics; + } + + /** + * Adds a {@link com.devonfw.tools.solicitor.model.inventory.RawLicense} to the given + * {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent}. + * + * @param appComponent a {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent} object. + * @param name a {@link java.lang.String} object. + * @param url a {@link java.lang.String} object. + * @param origin a {@link java.lang.String} object. + */ + public void addRawLicense(ApplicationComponent appComponent, String name, String url, String origin) { + + RawLicense mlic = this.modelFactory.newRawLicense(); + mlic.setApplicationComponent(appComponent); + mlic.setDeclaredLicense(name); + mlic.setLicenseUrl(url); + mlic.setOrigin(origin); + String trace; + trace = "+ Component/License info read from ComponentInfo data source"; + + mlic.setTrace(trace); + } + + /** + * This method sets the field modelFactory. + * + * @param modelFactory the new value of the field modelFactory + */ + @Autowired + public void setModelFactory(ModelFactory modelFactory) { + + this.modelFactory = modelFactory; + } + + /** + * This method sets the field componentInfoAdapters + * + * @param componentInfoAdapters new value of componentInfoAdapters. + */ + @Autowired + public void setComponentInfoAdapters(ComponentInfoAdapter[] componentInfoAdapters) { + + this.componentInfoAdapters = componentInfoAdapters; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java new file mode 100644 index 00000000..5f76694b --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java @@ -0,0 +1,19 @@ +package com.devonfw.tools.solicitor.componentinfo; + +/** + * License information within the {@link ComponentInfo} data structure. + * + */ +public interface LicenseInfo { + + /** + * @return spdxid + */ + String getSpdxid(); + + /** + * @return licenseFilePath + */ + String getLicenseFilePath(); + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java similarity index 59% rename from core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java rename to core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index 7c6a0f89..97a966db 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ComponentScancodeInfos.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -1,47 +1,49 @@ -package com.devonfw.tools.solicitor.scancode; +package com.devonfw.tools.solicitor.componentinfo.scancode; import java.util.Collection; import java.util.Collections; -import java.util.Map; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.LicenseInfo; + /** * Data structure for holding the scan information from scancode for a single package. */ -public class ComponentScancodeInfos { +public class ScancodeComponentInfo implements ComponentInfo { /** * Holds the info about a single found license. */ - public class LicenseInfo { + public class ScancodeLicenseInfo implements LicenseInfo { /** * Id for of the license. */ - public String id; + private String id; /** * SPDX-ID of the license. */ - public String spdxid; + private String spdxid; /** * The score of the license. */ - public double licenseScore; + private double licenseScore; /** * Path to the license file. */ - public String licenseFilePath; + private String licenseFilePath; /** * The score of the license file. */ - public double licenseFileScore; + private double licenseFileScore; /** * The constructor. @@ -53,14 +55,14 @@ public class LicenseInfo { * @param licenseFilePath the path to the license file * @param licenseFileScore the score of the license file */ - public LicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath, + public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath, double licenseFileScore) { super(); this.id = id; this.spdxid = spdxid; this.licenseScore = licenseScore; - if (licenseFileScore >= ComponentScancodeInfos.this.minLicensefilePercentage) { + if (licenseFileScore >= ScancodeComponentInfo.this.minLicensefilePercentage) { this.licenseFilePath = licenseFilePath; this.licenseFileScore = licenseFileScore; } else { @@ -68,13 +70,95 @@ public LicenseInfo(String id, String spdxid, String defaultUrl, double licenseSc this.licenseFileScore = 0.0; } } + + /** + * @return id + */ + public String getId() { + + return this.id; + } + + /** + * @return spdxid + */ + @Override + public String getSpdxid() { + + return this.spdxid; + } + + /** + * @return licenseScore + */ + public double getLicenseScore() { + + return this.licenseScore; + } + + /** + * @return licenseFilePath + */ + @Override + public String getLicenseFilePath() { + + return this.licenseFilePath; + } + + /** + * @return licenseFileScore + */ + public double getLicenseFileScore() { + + return this.licenseFileScore; + } + + /** + * @param id new value of {@link #getId}. + */ + public void setId(String id) { + + this.id = id; + } + + /** + * @param spdxid new value of {@link #getSpdxid}. + */ + public void setSpdxid(String spdxid) { + + this.spdxid = spdxid; + } + + /** + * @param licenseScore new value of {@link #getLicenseScore}. + */ + public void setLicenseScore(double licenseScore) { + + this.licenseScore = licenseScore; + } + + /** + * @param licenseFilePath new value of {@link #getLicenseFilePath}. + */ + public void setLicenseFilePath(String licenseFilePath) { + + this.licenseFilePath = licenseFilePath; + } + + /** + * @param licenseFileScore new value of {@link #getLicenseFileScore}. + */ + public void setLicenseFileScore(double licenseFileScore) { + + this.licenseFileScore = licenseFileScore; + } } private static final double MIN_NOTICEFILE_PERCENTAGE = 0.0; private SortedSet copyrights = new TreeSet<>(); - private SortedMap licenses = new TreeMap<>(); + private SortedMap licenses = new TreeMap<>(); private double noticeFileScore = 0; @@ -92,7 +176,7 @@ public LicenseInfo(String id, String spdxid, String defaultUrl, double licenseSc * @param minLicenseScore the minimum score to take license findings into account * @param minLicensefilePercentage the minimum percentage of license text to possibly use file as license file */ - public ComponentScancodeInfos(double minLicenseScore, double minLicensefilePercentage) { + public ScancodeComponentInfo(double minLicenseScore, double minLicensefilePercentage) { super(); this.minLicenseScore = minLicenseScore; @@ -114,6 +198,7 @@ public void addCopyright(String copyright) { * * @return the copyrights */ + @Override public Collection getCopyrights() { return Collections.unmodifiableCollection(this.copyrights); @@ -142,22 +227,22 @@ public void addLicense(String licenseId, String licenseName, String licenseDefau double fileScore) { if (this.licenses.containsKey(licenseId)) { - LicenseInfo existingLicenseInfo = this.licenses.get(licenseId); + ScancodeLicenseInfo existingLicenseInfo = this.licenses.get(licenseId); - double resultingScore = Math.max(existingLicenseInfo.licenseScore, score); - String resultingFilePath = existingLicenseInfo.licenseFilePath; - double resultingFileScore = existingLicenseInfo.licenseFileScore; - if (fileScore > existingLicenseInfo.licenseFileScore) { + double resultingScore = Math.max(existingLicenseInfo.getLicenseScore(), score); + String resultingFilePath = existingLicenseInfo.getLicenseFilePath(); + double resultingFileScore = existingLicenseInfo.getLicenseFileScore(); + if (fileScore > existingLicenseInfo.getLicenseFileScore()) { resultingFilePath = filePath; resultingFileScore = fileScore; } - this.licenses.put(licenseId, new LicenseInfo(licenseId, licenseName, licenseDefaultUrl, resultingScore, + this.licenses.put(licenseId, new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, resultingScore, resultingFilePath, resultingFileScore)); } else { if (score >= this.minLicenseScore) { this.licenses.put(licenseId, - new LicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, filePath, fileScore)); + new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, filePath, fileScore)); } } } @@ -167,9 +252,10 @@ public void addLicense(String licenseId, String licenseName, String licenseDefau * * @return all licenses */ - public Map getLicenses() { + @Override + public Collection getLicenses() { - return Collections.unmodifiableSortedMap(this.licenses); + return Collections.unmodifiableSortedMap(this.licenses).values(); } /** @@ -200,6 +286,7 @@ public void addNoticeFilePath(String path, double score) { * * @return path to the notice file */ + @Override public String getNoticeFilePath() { return this.noticeFilePath; @@ -210,6 +297,7 @@ public String getNoticeFilePath() { * * @return url to the license text */ + @Override public String getUrl() { return this.url; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java similarity index 75% rename from core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java rename to core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java index 3db81035..91067ad9 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeFileAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java @@ -1,4 +1,4 @@ -package com.devonfw.tools.solicitor.scancode; +package com.devonfw.tools.solicitor.componentinfo.scancode; import java.io.File; import java.io.FileInputStream; @@ -9,10 +9,14 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -20,10 +24,11 @@ /** * Adapter for reading Scancode information for a package from a file, applying any given curations and returning the - * information as a {@link ComponentScancodeInfos} object. + * information as a {@link ComponentInfo} object. */ @Component -public class ScancodeFileAdapter implements ScancodeAdapter { +@Order(ComponentInfoAdapter.DEFAULT_PRIO) +public class ScancodeFileAdapter implements ComponentInfoAdapter { private static final Logger LOG = LoggerFactory.getLogger(ScancodeFileAdapter.class); @@ -41,6 +46,10 @@ public class ScancodeFileAdapter implements ScancodeAdapter { private boolean curationsExistenceLogged; + private boolean featureFlag = false; + + private boolean featureLogged = false; + @Autowired private AllKindsPackageURLHandler packageURLHandler; @@ -52,6 +61,17 @@ public ScancodeFileAdapter() { } + /** + * Sets the feature flag for activating/deactivating this feature. + * + * @param featureFlag the flag + */ + @Value("${solicitor.feature-flag.scancode}") + public void setFeatureFlag(boolean featureFlag) { + + this.featureFlag = featureFlag; + } + /** * Sets repoBasePath. * @@ -98,25 +118,44 @@ public void setMinLicensefilePercentage(double minLicensefilePercentage) { /** * Retrieves the Scancode information and curations for a package identified by the given package URL. Returns the - * data as a {@link ComponentScancodeInfos} object. + * data as a {@link ScancodeComponentInfo} object. * * @param packageUrl The identifier of the package for which information is requested * @return the data derived from the scancode results after applying any defined curations. null is * returned if no data is available, - * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available - * no exception will be thrown. Instead null will be return in such a case. + * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no + * data available no exception will be thrown. Instead null will be return in such a case. */ @Override - public ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throws ScancodeException { + public ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException { - ComponentScancodeInfos componentScancodeInfos = determineScancodeInformation(packageUrl); - if (componentScancodeInfos == null) { + if (isFeatureActive()) { + + ScancodeComponentInfo componentScancodeInfos = determineScancodeInformation(packageUrl); + if (componentScancodeInfos == null) { + return null; + } + applyCurations(packageUrl, componentScancodeInfos); + + return componentScancodeInfos; + + } else { return null; } - applyCurations(packageUrl, componentScancodeInfos); - return componentScancodeInfos; + } + private boolean isFeatureActive() { + + if (!this.featureLogged) { + if (this.featureFlag) { + LOG.warn(LogMessages.SCANCODE_PROCESSOR_STARTING.msg()); + } else { + LOG.info(LogMessages.SCANCODE_FEATURE_DEACTIVATED.msg()); + } + this.featureLogged = true; + } + return this.featureFlag; } /** @@ -124,12 +163,12 @@ public ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throw * * @param packageUrl The package url of the package * @return the read scancode information, null if no information was found - * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available - * no exception will be thrown. Instead null will be return in such a case. + * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no + * data available no exception will be thrown. Instead null will be return in such a case. */ - private ComponentScancodeInfos determineScancodeInformation(String packageUrl) throws ScancodeException { + private ScancodeComponentInfo determineScancodeInformation(String packageUrl) throws ComponentInfoAdapterException { - ComponentScancodeInfos componentScancodeInfos = new ComponentScancodeInfos(this.minLicenseScore, + ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore, this.minLicensefilePercentage); String packagePathPart = this.packageURLHandler.pathFor(packageUrl); String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json"; @@ -153,10 +192,10 @@ private ComponentScancodeInfos determineScancodeInformation(String packageUrl) t + this.packageURLHandler.pathFor(packageUrl) + "/" + file.get("path").asText(), 100.0); } for (JsonNode cr : file.get("copyrights")) { - if(cr.has("copyright")) { + if (cr.has("copyright")) { componentScancodeInfos.addCopyright(cr.get("copyright").asText()); } else { - componentScancodeInfos.addCopyright(cr.get("value").asText()); + componentScancodeInfos.addCopyright(cr.get("value").asText()); } } @@ -178,7 +217,7 @@ private ComponentScancodeInfos determineScancodeInformation(String packageUrl) t componentScancodeInfos.getNoticeFilePath() != null ? 1 : 0); } catch (IOException e) { - throw new ScancodeException("Could not read Scancode JSON", e); + throw new ComponentInfoAdapterException("Could not read Scancode JSON", e); } return componentScancodeInfos; } @@ -189,10 +228,10 @@ private ComponentScancodeInfos determineScancodeInformation(String packageUrl) t * * @param packageUrl the identifier of the package * @param componentScancodeInfos the componentScancodeInfos to curate - * @throws ScancodeException if the curations could not be read + * @throws ComponentInfoAdapterException if the curations could not be read */ - private void applyCurations(String packageUrl, ComponentScancodeInfos componentScancodeInfos) - throws ScancodeException { + private void applyCurations(String packageUrl, ScancodeComponentInfo componentScancodeInfos) + throws ComponentInfoAdapterException { String packagePathPart = this.packageURLHandler.pathFor(packageUrl); @@ -216,7 +255,7 @@ private void applyCurations(String packageUrl, ComponentScancodeInfos componentS for (JsonNode curations : curationsObj.get("artifacts")) { String component = curations.get("name").asText(); if (component.equals(packagePathPart)) { - ComponentScancodeInfos oneComponent = componentScancodeInfos; + ScancodeComponentInfo oneComponent = componentScancodeInfos; if (curations.get("copyrights") != null) { oneComponent.clearCopyrights(); int authorCount = curations.get("copyrights").size(); @@ -240,7 +279,7 @@ private void applyCurations(String packageUrl, ComponentScancodeInfos componentS } } catch (IOException e) { - throw new ScancodeException("Could not read Curations JSON", e); + throw new ComponentInfoAdapterException("Could not read Curations JSON", e); } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java deleted file mode 100644 index 997b9926..00000000 --- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeAdapter.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.devonfw.tools.solicitor.scancode; - -/** - * Adapter for reading Scancode information for a package, applying any given curations and returning the information as - * a {@link ComponentScancodeInfos} object. - */ -public interface ScancodeAdapter { - - /** - * Retrieves the Scancode information and curations for a package identified by the given package URL. Returns the - * data as a {@link ComponentScancodeInfos} object. - * - * @param packageUrl The identifier of the package for which information is requested - * @return the data derived from the scancode results after applying any defined curations. null is - * returned if no data is available, - * @throws ScancodeException if there was an exception when reading the data. In case that there is no data available - * no exception will be thrown. Instead null will be return in such a case. - */ - ComponentScancodeInfos getComponentScancodeInfos(String packageUrl) throws ScancodeException; - -} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java deleted file mode 100644 index 9b9eb761..00000000 --- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeException.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.devonfw.tools.solicitor.scancode; - -/** - * Exception indicating that the Scancode information could not be read. - */ -public class ScancodeException extends Exception { - - /** - * A constructor. - */ - public ScancodeException() { - - } - - /** - * A constructor. - * - * @param message the message of the exception - */ - public ScancodeException(String message) { - - super(message); - } - - /** - * A constructor. - * - * @param message the message of the exception - * @param cause the underlying {@link Throwable}. - */ - public ScancodeException(String message, Throwable cause) { - - super(message, cause); - } - -} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java deleted file mode 100644 index 9f2edece..00000000 --- a/core/src/main/java/com/devonfw/tools/solicitor/scancode/ScancodeInventoryProcessor.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.devonfw.tools.solicitor.scancode; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - -import com.devonfw.tools.solicitor.InventoryProcessor; -import com.devonfw.tools.solicitor.common.LogMessages; -import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; -import com.devonfw.tools.solicitor.model.ModelFactory; -import com.devonfw.tools.solicitor.model.ModelRoot; -import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; -import com.devonfw.tools.solicitor.model.inventory.RawLicense; -import com.devonfw.tools.solicitor.model.masterdata.Application; -import com.devonfw.tools.solicitor.scancode.ComponentScancodeInfos.LicenseInfo; - -/** - * An {@link InventoryProcessor} which looks up license information for the found application components / packages in - * the scancode file store. If license information is found then the license, copyright and notice file information of - * the model will be replaced by the data obtained from scancode. - * - */ -@Component -@Order(InventoryProcessor.BEFORE_RULE_ENGINE) -public class ScancodeInventoryProcessor implements InventoryProcessor { - - private static class Statistics { - public int componentsTotal; - - public int componentsWithScancodeInfos; - - public void add(Statistics statistics) { - - this.componentsTotal += statistics.componentsTotal; - this.componentsWithScancodeInfos += statistics.componentsWithScancodeInfos; - } - } - - /** - * Origin data for raw license objects created by this Class. - */ - private static final String ORIGIN_SCANCODE = "scancode"; - - private static final Logger LOG = LoggerFactory.getLogger(ScancodeInventoryProcessor.class); - - private boolean featureFlag = false; - - private ScancodeAdapter scancodeAdapter; - - private ModelFactory modelFactory; - - /** - * Sets the feature flag for activating/deactivating this feature. - * - * @param featureFlag the flag - */ - @Value("${solicitor.feature-flag.scancode}") - public void setFeatureFlag(boolean featureFlag) { - - this.featureFlag = featureFlag; - } - - /** - * The constructor. - */ - public ScancodeInventoryProcessor() { - - } - - @Override - public void processInventory(ModelRoot modelRoot) { - - if (!this.featureFlag) { - LOG.info(LogMessages.SCANCODE_FEATURE_DEACTIVATED.msg()); - } else { - LOG.warn(LogMessages.SCANCODE_PROCESSOR_STARTING.msg()); - - Statistics overall = new Statistics(); - for (Application application : modelRoot.getEngagement().getApplications()) { - for (ApplicationComponent ac : application.getApplicationComponents()) { - Statistics single = processApplicationComponent(ac); - overall.add(single); - } - } - LOG.info(LogMessages.SCANCODE_INFO_READ.msg(), overall.componentsWithScancodeInfos, overall.componentsTotal); - } - } - - /** - * Process a single {@link ApplicationComponent}. - * - * @param ac application component - * @return processing statistics - */ - private Statistics processApplicationComponent(ApplicationComponent ac) { - - Statistics statistics = new Statistics(); - statistics.componentsTotal = 1; - - if (ac.getPackageUrl() != null) { - ComponentScancodeInfos componentScancodeInfos; - try { - componentScancodeInfos = this.scancodeAdapter.getComponentScancodeInfos(ac.getPackageUrl()); - } catch (ScancodeException e) { - throw new SolicitorRuntimeException("Exception when reading scancode file", e); - } - if (componentScancodeInfos != null) { - statistics.componentsWithScancodeInfos = 1; - ac.removeAllRawLicenses(); - - if (componentScancodeInfos.getNoticeFilePath() != null) { - ac.setNoticeFileUrl(componentScancodeInfos.getNoticeFilePath()); - } - - for (LicenseInfo li : componentScancodeInfos.getLicenses().values()) { - addRawLicense(ac, li.spdxid, li.licenseFilePath, ORIGIN_SCANCODE); - } - String copyrights = String.join("\n", componentScancodeInfos.getCopyrights()); - ac.setCopyrights(copyrights); - // check whether VendorUrl is included in input file or not - if (componentScancodeInfos.getUrl() != null) { - ac.setOssHomepage(componentScancodeInfos.getUrl()); - } - } else { - // no scancode info found for ac - } - } else { - // can this happen? - } - return statistics; - } - - /** - * Performs logging. - * - * @param sourceUrl the URL from where the inventory data was read - * @param application the application - * @param readComponents number of read ApplicationComponents - * @param readLicenses number of read RawLicenses - */ - public void doLogging(String sourceUrl, Application application, int readComponents, int readLicenses) { - - LOG.info(LogMessages.READING_INVENTORY.msg(), readComponents, readLicenses, application.getName(), sourceUrl); - } - - /** - * Adds a {@link com.devonfw.tools.solicitor.model.inventory.RawLicense} to the given - * {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent}. - * - * @param appComponent a {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent} object. - * @param name a {@link java.lang.String} object. - * @param url a {@link java.lang.String} object. - * @param origin a {@link java.lang.String} object. - */ - public void addRawLicense(ApplicationComponent appComponent, String name, String url, String origin) { - - RawLicense mlic = this.modelFactory.newRawLicense(); - mlic.setApplicationComponent(appComponent); - mlic.setDeclaredLicense(name); - mlic.setLicenseUrl(url); - mlic.setOrigin(origin); - String trace; - trace = "+ Component/License info read from scancode information"; - - mlic.setTrace(trace); - } - - /** - * This method sets the field modelFactory. - * - * @param modelFactory the new value of the field modelFactory - */ - @Autowired - public void setModelFactory(ModelFactory modelFactory) { - - this.modelFactory = modelFactory; - } - - /** - * This method sets the field scancodeAdapter - * - * @param scancodeAdapter new value of scancodeAdapter. - */ - @Autowired - public void setScancodeAdapter(ScancodeAdapter scancodeAdapter) { - - this.scancodeAdapter = scancodeAdapter; - } - -} diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 07d8b092..7bd1b9f9 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1528,11 +1528,31 @@ The Extension will be activated by referencing it as follows when starting _Soli [listing] java -Dloader.path=path/to/the/extension.zip -jar solicitor.jar +=== Java Extensions +It is also possible to extend the functionality of _Solicitor_ within an extension by implementing Spring Beans +which implement certain interfaces. As the resources contained in the extension are included into _Solicitors_ classpath those beans +might be discovered through the Spring component scan mechanism and thus be activated. + +NOTE: The Spring components scanning mechanisms by default searches only in package `com.devonfw.tools.solicitor` (and subpackages). You either need to define the extension classes in these packages or create a specific configuration class in this package which has an +appropriate `@ComponentScan` annotation which points to your packages. + +WARNING: Extending Solicitor via Java is an advanced topic. Only the Interfaces given below should be used. Even those should +be regarded as unstable and might change without notice. For any details on the interfaces see the _Solicitor_ source code and corresponding Javadoc. + +==== Extension Interfaces +===== `com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter` +A spring bean implementing this interface might provide `ComponentInfo`/`LicenseInfo` data for `ApplicationComponents` identified +by their packageUrl. (The buildin implementation of this interface is reading such component info from scancode result files +from the local file system, see <>.) Alternative implementations might e.g. get this information +from a corporate server or even a public service available on the internet. + [appendix] == Release Notes Changes in 1.4.0:: +* https://github.com/devonfw/solicitor/issues/139: Provide extension interface to allow reading information about components/licenses +from alternative sources. See <>. * https://github.com/devonfw/solicitor/issues/137: Internal restructuration of Solicitor modules which allows Solicitor code to be used as dependency in other projects. * https://github.com/devonfw/solicitor/issues/129: Added spellcheck support within documentation for run-together words like camel cased ones. * https://github.com/devonfw/solicitor/issues/130: Fixed a bug where the guessedLicenseUrl and guessedLicenseUrlAuditInfo fields were not filled correctly in the aggregated inventory. From 3d2c0c2566e38e8ad0b1f3a79a5ba51a90dab3f7 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 11 Nov 2022 18:08:37 +0100 Subject: [PATCH 014/139] Avoid failure of report generation if PackageURL is not set. (#142) --- .../solicitor/templates/ScancodeScanScript.vm | 34 +++++----- .../solicitor/templates/ScancodeScript.vm | 66 ++++++++++--------- documentation/master-solicitor.asciidoc | 1 + 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm index 3229134a..2cb0d707 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScanScript.vm @@ -6,21 +6,25 @@ echo {\"artifacts\": [ > scancodeOutput/scancodeOut.json dir=$(pwd)/Source #foreach ($artifact in $ARTIFACTS) -if [ ! -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] || [ ! -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed ] -then - rm $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed - rm $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json - if [ -d $dir/$purlhandler.pathFor($artifact.packageUrl)/sources ] && [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/sources.completed ] - then - scancode $scancodeoptions --json-pp $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json $dir/$purlhandler.pathFor($artifact.packageUrl)/sources - touch $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed - fi -fi -if [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] && [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed ] -then - cat $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json >> $so/scancodeOut.json - printf "\n," >> $so/scancodeOut.json -fi + #if ( $artifact.packageUrl == "NA" ) + echo "PackageUrl is missing for Artifact Group: $artifact.groupId -- Artifact: $artifact.artifactId -- Version: $artifact.version -- NO DATA TO SCAN" + #else + if [ ! -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] || [ ! -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed ] + then + rm $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed + rm $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json + if [ -d $dir/$purlhandler.pathFor($artifact.packageUrl)/sources ] && [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/sources.completed ] + then + scancode $scancodeoptions --json-pp $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json $dir/$purlhandler.pathFor($artifact.packageUrl)/sources + touch $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed + fi + fi + if [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json ] && [ -f $dir/$purlhandler.pathFor($artifact.packageUrl)/scancodeScan.completed ] + then + cat $dir/$purlhandler.pathFor($artifact.packageUrl)/scancode.json >> $so/scancodeOut.json + printf "\n," >> $so/scancodeOut.json + fi + #end #end printf "END" >> $so/scancodeOut.json diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm index 3ae23913..d5aa4b8a 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm @@ -17,40 +17,44 @@ echo "These artifacts need to be downloaded manually:" > need-to-retrieve-manual #foreach ($artifact in $ARTIFACTS) # Group: $artifact.groupId -- Artifact: $artifact.artifactId -- Version: $artifact.version -- License(s): $artifact.effectiveNormalizedLicense -- includeSource: $artifact.includeSource - if [ ! -d "$purlhandler.pathFor($artifact.packageUrl)" ] - then - mkdir -p $purlhandler.pathFor($artifact.packageUrl) - fi - if [ ! -f "$purlhandler.pathFor($artifact.packageUrl)/sources.completed" ] || [ ! -d "$purlhandler.pathFor($artifact.packageUrl)/sources" ] - then - cd $purlhandler.pathFor($artifact.packageUrl) - rm sources.completed - rm -r sources - rm sources.jar - curl -# $purlhandler.sourceDownloadUrlFor($artifact.packageUrl) -o sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) - #if ( $purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) == "jar" ) - unzip -q -d sources sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) - #elseif($purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) == "tgz") - mkdir sources - tar -xvzf sources.tgz -C sources - #elseif($purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) == "tar.gz") - mkdir sources - tar -xvzf sources.tar.gz -C sources - #else - unzip -q nonexisting - #end - if [ $? -ne 0 ] + #if ( $artifact.packageUrl == "NA" ) + echo "Artifact Group: $artifact.groupId -- Artifact: $artifact.artifactId -- Version: $artifact.version" >> need-to-retrieve-manually.txt + #else + if [ ! -d "$purlhandler.pathFor($artifact.packageUrl)" ] then - touch sources.failed - rm sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) + mkdir -p $purlhandler.pathFor($artifact.packageUrl) + fi + if [ ! -f "$purlhandler.pathFor($artifact.packageUrl)/sources.completed" ] || [ ! -d "$purlhandler.pathFor($artifact.packageUrl)/sources" ] + then + cd $purlhandler.pathFor($artifact.packageUrl) + rm sources.completed rm -r sources - #[[cd $SD]]# - echo "Artifact $purlhandler.sourceDownloadUrlFor($artifact.packageUrl)" >> need-to-retrieve-manually.txt - else - touch sources.completed - #[[cd $SD]]# + rm sources.jar + curl -# $purlhandler.sourceDownloadUrlFor($artifact.packageUrl) -o sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) + #if ( $purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) == "jar" ) + unzip -q -d sources sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) + #elseif($purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) == "tgz") + mkdir sources + tar -xvzf sources.tgz -C sources + #elseif($purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) == "tar.gz") + mkdir sources + tar -xvzf sources.tar.gz -C sources + #else + unzip -q nonexisting + #end + if [ $? -ne 0 ] + then + touch sources.failed + rm sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) + rm -r sources + #[[cd $SD]]# + echo "Artifact $purlhandler.sourceDownloadUrlFor($artifact.packageUrl)" >> need-to-retrieve-manually.txt + else + touch sources.completed + #[[cd $SD]]# + fi fi - fi + #end #end cd .. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 7bd1b9f9..45880be5 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1551,6 +1551,7 @@ from a corporate server or even a public service available on the internet. == Release Notes Changes in 1.4.0:: +* https://github.com/devonfw/solicitor/issues/141: Improved robustness of report generation in cases where PackageURL can not be determined (e.g. if data originates from CSV reader). * https://github.com/devonfw/solicitor/issues/139: Provide extension interface to allow reading information about components/licenses from alternative sources. See <>. * https://github.com/devonfw/solicitor/issues/137: Internal restructuration of Solicitor modules which allows Solicitor code to be used as dependency in other projects. From 1856dd5cf6621d65cdff31e7824ddf6453b39f73 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 14 Nov 2022 10:57:36 +0100 Subject: [PATCH 015/139] fallback rule moved to the end of the list (#144) --- .../rules/LicenseNameMappingSample.xls | Bin 77824 -> 77824 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls index ac99dcb1df9b179fe0ca5581bda71cc8ec26347f..22a2abcb6de625cbc30a13d56aa0222876130aac 100644 GIT binary patch delta 4853 zcmZwKYj6`)6ae6J)1*z(JX%^y0o!bl@(`#zDkw-v3M~nwC8aG`r9y!Um4{L)_@YV0 zq6n120t=#2V89W<2P@^-*9`p7aU7Y^0UeoXQGfXd^^epkc<%0oB%4#%X79J>p8MFF zyW4P|G2CbTa+T51yW>RRV9zb~L&vRh`@dp?aXb4{yk~sCcFFflTMTTexY0afnhyXn z@TkPYfXDAffL#1c!^2m%a&2A11YzO2`ptC>03NjPnR1@3%UpQQGM1$~^iiVMx-6lC7y9$r{s%{;D4`B;td3fymJ-^i>d;}g12n0P+C<{DR=`*yww##QL#|zD;5G?q=jY0g1<~H z`ztElZnwO+y2P3L%{G9a_W_JM22d>nTw-#U=Z>x(eix& zmM^+mTV@ULnZc41%!V{2|9yzJB)qIQTxs0I{?-g#20jjbbP8c+EyLtJ`V3YgI+ZY+ zmZ3Qz1Fr0ZJ~#>EUz8La#f^jvL)0ty8-KL9;a@e3Rw!bbs)_D4{V1}C=%IvVYZ*2s zWEf6Fk09((Ekkcx#<=+2@y$TrW0z|3VA~(5q4;n&UV5M#;7GzABWx65d4!FQwU9Qm zV`5RWv4o8y>~X@z6E=ac&_n|B344ODNoe}XolMvi!ltSg!Vmn>MqGfW`h-ssHjS|9 zggr&r({UD3zw|TWP_>zaJww~kH{E4q0$TFSrMd*>wo;~l>?S!=$n5^3g zR}i+6uvLWB5%yxNMHb=eSkxBb8p7%cdx@~Mgsmeiw4T5XguP7ID}-$%tbwpis>LtD zMk4T4!Zs7Og|OEMdp*vyMfgS>s*CVV!rmfmD`8E9@u9}9Exrhs%eT9Kv7Gys8AALm zEQ}%gwcAYC+k|Z+>>a{d2-^-2N@)09BJe%Jb`bVHVLJ)i_0ah2XzPCWLyTH0VS5N` zBWy2W`}7vV=@<2gwx6&AgdHSIBJ2=hhtWd%`~CqDc!aPI3Hyk!j|n>(HF@gy{pq?_ z`eV_{4{y$K!cM3rmiOsjhEEdFrwIE*%Wy0q!>2^_X~I6!GW?a0;VcpTd5nd%W%q@a z!P_4hMVKv6UQND=i&pI%VX~HCZ%l@HN`~`9bO&Jso@V_*dqP>f)89i6%B|Oq5LES1R%$Vntz<(rUL-u|ryK zVtE<-uJHQZ*oq%+!dz&VPMX-vng9eKhzH-NdoU<=E9cfr*fqtB3_mcdQ)#;gPqFRg zYTG?Z+ZUx-$t*9#?=f$?M`@c+4^N80;aX1@PDrm3?8A}q>FEVN_xzEmmiXNBhoxHL zz5iP)UBh}sz&HMzSnpT-9E$@ffkML&=t$-}mj@4QvuB(#GO(jgOazzIp$ z@fvmwuH!$G=`eO6-*O=*g>5h;bwcNbRvW8HH%-c5&?4QXK1M>mcjQ{`u delta 5063 zcmZwLYj6`)6bJBg(1R+Lu`$t~PawVWu-ZCZ9MIqgP=NYX;9#-H^L5&@q%NSokWpq2Xk z@k71?LVTx{`ZIlAsuSGNL9&^rFvBls1U1RYbk5LD`1|OmwC~kXO(0Xd>G!gw`qQ;w zKpvIy(U3sEIGSl3$-zb?2CE6kIqCyhDlF4CA~AiZI#ueIJ;o<`Eq*aaHpFAhKg0JJRxOdEq?D&yI zMJYMatweSEh&tzR6~b7!!NUXqXomS6iafZYe| zeqe)v4FMJz3h)774+49L&7Rz0z=i{RST}2ljR1iofsF$82(U+iJ!Y|pUQVMesM{D| zV}U&mY#gxhHmh;?C^z14_uQ3fo%fzlEsW7eA$9}5xxTK zRbZQe)c}*1YOXC=gp1-+D>4*fYk!q4QvmvI-*FgEbaw?`+#Y{_5(Wr>|l?{{2S?y^11@*1)BCsZ4&A?iKwc1Q>h4t}{s||Uj(eiK$`RF45 z)m_Xl2mh5UTSSVlFe5%$D7oT4Biw+b2D z)DtY1%E9%^Rd6#0&!9_2R5MRotfp#K6U+OT9I~0@#NYHpxu`@ zuiz-)A3$>j8%`eJA zu3!5K8XlXJEoSTg%OgMj$QG;fy7b}ro9(6ob)2Hh;+0{d{A*dU>Ksw4rB=>u$Q7yo E17#-2y#N3J From 69e1c10ba825ea4f825dbb6edf63702401c4c836 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 15 Nov 2022 22:42:51 +0100 Subject: [PATCH 016/139] Version 1.4.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index ca6e0716..21b015ad 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.4.0-SNAPSHOT + 1.4.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 52a676d3..12afc827 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.4.0-SNAPSHOT + 1.4.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index b0b4adab..7e8b5456 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.4.0-SNAPSHOT + 1.4.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index cbe5b194..5f3a458b 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.4.0-SNAPSHOT + 1.4.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 0264f875..e46c0e5c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.4.0-SNAPSHOT + 1.4.0 pom Solicitor Aggregator From 86265f69334bdb116a3553de96d47bd99e03f115 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 15 Nov 2022 22:56:43 +0100 Subject: [PATCH 017/139] Set Version to SNAPSHOT of next minor version --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 3 +++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 21b015ad..be80f9e9 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.4.0 + 1.5.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 12afc827..5d5b0093 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.4.0 + 1.5.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 45880be5..acb3af77 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1550,6 +1550,9 @@ from a corporate server or even a public service available on the internet. [appendix] == Release Notes +Changes in 1.5.0:: + + Changes in 1.4.0:: * https://github.com/devonfw/solicitor/issues/141: Improved robustness of report generation in cases where PackageURL can not be determined (e.g. if data originates from CSV reader). * https://github.com/devonfw/solicitor/issues/139: Provide extension interface to allow reading information about components/licenses diff --git a/documentation/pom.xml b/documentation/pom.xml index 7e8b5456..13dcf775 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.4.0 + 1.5.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 5f3a458b..16049879 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.4.0 + 1.5.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index e46c0e5c..6549216e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.4.0 + 1.5.0-SNAPSHOT pom Solicitor Aggregator From e3f28892bbfeacd52c06215cc22aa6246b57d46e Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 7 Dec 2022 18:33:09 +0100 Subject: [PATCH 018/139] Allow multiple NormalizedLicense entries with same id per ApplicationComponent if the declared license differs. This allows to assign multiple licenses of same type (e.g. MIT) to a component and also will allow multiple "UNKNOWN" licenses to be reported for the same component. (#145) --- .../rules/LicenseAssignmentV2Sample.xls | Bin 61440 -> 61952 bytes .../rule_templates/LicenseAssignment.drt | 5 +++-- .../rule_templates/LicenseAssignmentV2.drt | 3 ++- .../rule_templates/LicenseNameMapping.drt | 13 +++++++------ documentation/master-solicitor.asciidoc | 2 +- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls index dfdc7ad23235e7226fd0885bc3267ab877b038a5..09a8cbde5a5e07ba03f0946f4e9d0aa6424e0298 100644 GIT binary patch delta 3391 zcmZvee{54#6vw}>?bfyHkI~IBevG{_kO9&$e}D{}9cvwft=(@0WuzOOtCViGn^l8p z4H~8p!hC8(|54C?lmx;{PzfP~U;;)-{KKDMAW@0&52Fcx=pxMX-fPz%@8spad(Y>d zd+&MYo_Fp|-880tHJ&mVEaL#~&d$z0E87j92+POPqJm=7j#HVSGD+n*DhVogr@QOk zFzt5!GIYH8&V?U@)Q-g`yRAP;anq}YyWhz(ri(^P>TQ?81f$frYvXClbwM~G|7>*$ z!npKTX|4LMvR2@TK=T=(z)ESWtyJx(lp=Gj!)nW8ud*&sl?6rloUK|_tdRb++0(5u zyVk0-30Q}7hV;B1du`2lkTJ%gow8>P+LVQgK|k3QgKnwaC5|=f zN>!5IVWF1CUMWQ#)Gxp2xKz6KCMsxBK%fU*Y7;1K5cic-U|&gER;ym@yB)YC4=mi| zkVfiVt{!!iiN-57&we#BE5Ba9%4KNjm(Dc3X}(dhi)Q>t?%sH{Jh>V02l|>?1D%}# zzv%T1_&Nezoj!k`*y8t!-uB+Up7zcCeeKlQ(cbFw_xi;8R3 z(0Zk==DK7z1w}Y0DmICGgL^_^d@>di6CrUp7!^l@(cO`d7>vZnNREf%Vq!Ez-4p6N z92cWwVmLaYyu?^AA(925XgoBFk5Phm8UY*mqxyben(9}m{)g&oR0qf*J01Y8CaLiy zHJZtDpFRkzHUg$~!1E^H)Oui9G0^ugaGC1q4L}iXM1K=-h3bfbC^Kuod`r9&p%80b33X`)ISjmoEmkIb3hiUom+E`1^DE zLCym=E=VcYYDEU=LgZt1^N4H>ui<@L!K@+`SR@S(&| zrkdqMJ|oYfkdc>ltcYRceVv{;mnDTRbz$W{DO|+DFxoDiUMa(r04ki1wpPcIw9h)| zWEjN({w?qf#e|Nz7)CLp(^GN=&6lJq>;8K*n8zv^HlJZt3|o-Lwy2(7n8$W%a8VxA zVqr1Esu@N}MfY_z8k0}#uQM&WLor!Qaod9r2y2psq~m~e{lEshMjY_sLn2G2`qdF; zb;SGW5oU_>9vq|o)RZ>DjLe9OQ~mPt*KRrI|4x317%t)6x#tYd;E=TRXknb!YWC4P z>-?n*t7F(QhEd+s`QYAMmJ~EcQwr57)-!A+!&WhDbq<5zRCoA3h9%eJz*O28H^Ukj zww7U(jB`Fj`#8$S#7vZ$6|ZWHa`$FT;Ec zdn}hF)7#&c3)5^1!`d0Pm0{Z$*1@o3CjGWWZ13(cAhe0Fo&!cm5W8TJgr_Ao5Mu!g-1j52IYWAfBxkFZENOOy+|a@%q^j?y*A cZTU<{9 delta 3112 zcmZwJeQXnD90&04bM4w*+iSb6blr4dS#`)jz!#8h%npXIA?yVZQDIK;1y_f-`4WS< zszGIcDBDR)mY5(Rn&>~qONcQ8jUh5(#6$x@!NeG&@gL(K#2|lAfA`YfwfF1g`aI9) zd3yKU^Zf34TKL^p_|^BRUy}C$xLzz4pVbxV6Ol2p#<`#PIXcJboS^f0I$xmk`qW_S zw0}=LeX{+#S4S_3<#QLlxH5538}}cSCeQ2F{9pTIjC^dE@M%+zbbKaX7UCoQmvEgB zXS9DJ8S^_GPU5hj>q#&$rS(N4W<^TNmve8Mxtx*?H=2!w(Reo6Y&J}4e@7KNmr$%+ zDk`uNvyy$?oRV2^rbcwrgY3l!@>qfSxJ;E@&b=GmeSD+9=Y}t3)T~Uu zpwBDx9D)ucH_)_NWr=c^YJ1CNAAIPII`w|kV7kF62M|EkRSqJE6LF_LL?d>h%_+-} zhmg%V5JniiDW@Dk1k?3SIZB<(HacYmGz8sFnFc~+wTahO(exOff>@7`lrSG4j!L6h zK(*0~c&vsonki4apxab2VBXk>tLmG`jM3D3+C>;MVy*gmGz1<~X(T)tXN*Dy|5c3) zg^$PT7^`P2X_-oiGyL-`tgsYg4UDB3Ypk%1=14bH*e(lis6ZD@xcS{TbRb~9tQxJ?z-khZ$fe!Uhkb}M6x z8EbPHMBJRqC5)*{U0AR?a~osJ7+cO5tve!ThG|NZd2{)-{3~uHZs=BSRZ4X7`u-#bu)uo80%+jfU!Zwh8WvwnH6HC zq29(c+|JnjjO}3T0mgP#m}9&IW4jpJ&De028Ea`ypZ!LWs)BH&s-dx) zKg!r+jO}6UamJo-TPX;ibfX=FPcgQav8NduWh~FwvS%0^V{D&g`oCv$A{L^3g#P=` QHyt6p@7u+xW4|T-2V+pn&j0`b diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt index 7e31d760..44b6c00a 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt +++ b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt @@ -76,10 +76,11 @@ rule "License Assignment @{ruleId} - add given NormalizedLicense" salience -@{ro $rl : RawLicense( specialHandling == false, ModelHelper.match( declaredLicense, "@{declaredLicense}" ), - $licUrl : licenseUrl, + $decLicense : declaredLicense, + $licUrl : licenseUrl, ModelHelper.match( licenseUrl, "@{url}" ), applicationComponent == $ac ) - not( NormalizedLicense(applicationComponent == $ac, normalizedLicense == "@{normalizedLicense}") ) // differs from above + not( NormalizedLicense(applicationComponent == $ac, declaredLicense == $decLicense, licenseUrl == $licUrl, normalizedLicense == "@{normalizedLicense}") ) // differs from above then NormalizedLicense normalizedLicense = ModelHelper.newNormalizedLicense($rl ); diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt index 5fd79b30..4ebf0587 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt +++ b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignmentV2.drt @@ -79,10 +79,11 @@ rule "License Assignment @{ruleId} - add given NormalizedLicense" salience -@{ro specialHandling == false, ModelHelper.match( origin, "@{origin}" ), ModelHelper.match( declaredLicense, "@{declaredLicense}" ), + $decLicense : declaredLicense, $licUrl : licenseUrl, ModelHelper.match( licenseUrl, "@{url}" ), applicationComponent == $ac ) - not( NormalizedLicense(applicationComponent == $ac, normalizedLicense == "@{normalizedLicense}") ) // differs from above + not( NormalizedLicense(applicationComponent == $ac, declaredLicense == $decLicense, licenseUrl == $licUrl, normalizedLicense == "@{normalizedLicense}") ) // differs from above then NormalizedLicense normalizedLicense = ModelHelper.newNormalizedLicense($rl ); diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseNameMapping.drt b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseNameMapping.drt index 76be068f..603bcad4 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseNameMapping.drt +++ b/core/src/main/resources/com/devonfw/tools/solicitor/rules/rule_templates/LicenseNameMapping.drt @@ -22,18 +22,19 @@ rule "Name Normalization @{ruleId}" salience -@{row.rowNumber} $ac : ApplicationComponent() $rl : RawLicense( specialHandling == false, - ModelHelper.match(declaredLicense, "@{declared}" ), - ModelHelper.match(licenseUrl, "@{url}" ), - $licUrl : licenseUrl, - applicationComponent == $ac ) - not( NormalizedLicense(applicationComponent == $ac, normalizedLicense == "@{normalized}" ) ) + ModelHelper.match(declaredLicense, "@{declared}" ), + ModelHelper.match(licenseUrl, "@{url}" ), + $decLicense : declaredLicense, + $licUrl : licenseUrl, + applicationComponent == $ac ) + not( NormalizedLicense(applicationComponent == $ac, declaredLicense == $decLicense, licenseUrl == $licUrl, normalizedLicense == "@{normalized}" ) ) then NormalizedLicense normalizedLicense = ModelHelper.newNormalizedLicense($rl ); normalizedLicense.setNormalizedLicenseType("@{type}"); normalizedLicense.setNormalizedLicense("@{normalized}"); normalizedLicense.setNormalizedLicenseUrl($licUrl); - // Create trace entry + // Create trace entry AuditEntryBuilder aeb = AuditEntryBuilder.instance(); aeb.withRuleId("@{ruleId}") .withMatching("declaredLicense", "@{declared}" ) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index acb3af77..c4473dc7 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1551,7 +1551,7 @@ from a corporate server or even a public service available on the internet. == Release Notes Changes in 1.5.0:: - +* https://github.com/devonfw/solicitor/issues/6: Fixed the bug by allowing multiple `NormalizedLicense` entries with same id per `ApplicationComponent` if the declared license differs. This allows to assign multiple licenses of same type (e.g. MIT) to a component and also will allow multiple "UNKNOWN" licenses to be reported for the same component. Note that as a side effect additional and unexpected `NormalizedLicense` entries might now be created. This might be caused from multiple `LicenseAssignment*.xls` rules firing for different `RawLicense` entries in the same `ApplicationComponent` and resulting in identical `NormalizedLicense` id. In this case it is necessary to restrict those different rules to only fire for specific `RawLicense` entries. Changes in 1.4.0:: * https://github.com/devonfw/solicitor/issues/141: Improved robustness of report generation in cases where PackageURL can not be determined (e.g. if data originates from CSV reader). From fa4149a6557e353147a8dd2b4f3f3ddbc6ff7079 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 7 Dec 2022 18:38:46 +0100 Subject: [PATCH 019/139] Version 1.5.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index be80f9e9..2cb238b7 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.5.0-SNAPSHOT + 1.5.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 5d5b0093..da324051 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.5.0-SNAPSHOT + 1.5.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 13dcf775..7dabedce 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.5.0-SNAPSHOT + 1.5.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 16049879..e6945911 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.5.0-SNAPSHOT + 1.5.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 6549216e..2b28351b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.5.0-SNAPSHOT + 1.5.0 pom Solicitor Aggregator From 84bd8a77b83eadc758eb395a35f22b7361a48c7e Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 13 Dec 2022 14:09:14 +0100 Subject: [PATCH 020/139] Set Version to SNAPSHOT of next minor version --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 2cb238b7..e8cfacc6 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.5.0 + 1.6.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index da324051..ae6e8d6f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.5.0 + 1.6.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index c4473dc7..4267b3e8 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1550,6 +1550,8 @@ from a corporate server or even a public service available on the internet. [appendix] == Release Notes +Changes in 1.6.0:: + Changes in 1.5.0:: * https://github.com/devonfw/solicitor/issues/6: Fixed the bug by allowing multiple `NormalizedLicense` entries with same id per `ApplicationComponent` if the declared license differs. This allows to assign multiple licenses of same type (e.g. MIT) to a component and also will allow multiple "UNKNOWN" licenses to be reported for the same component. Note that as a side effect additional and unexpected `NormalizedLicense` entries might now be created. This might be caused from multiple `LicenseAssignment*.xls` rules firing for different `RawLicense` entries in the same `ApplicationComponent` and resulting in identical `NormalizedLicense` id. In this case it is necessary to restrict those different rules to only fire for specific `RawLicense` entries. diff --git a/documentation/pom.xml b/documentation/pom.xml index 7dabedce..6aaf526f 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.5.0 + 1.6.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index e6945911..f7151c90 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.5.0 + 1.6.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 2b28351b..0f9d290a 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.5.0 + 1.6.0-SNAPSHOT pom Solicitor Aggregator From de174535ccddbeb6c4be4729e33247d1e75e0be1 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 13 Dec 2022 14:22:02 +0100 Subject: [PATCH 021/139] Reset velocity engine on initialization of each template (#147) --- .../tools/solicitor/writer/velocity/VelocityWriter.java | 3 ++- documentation/master-solicitor.asciidoc | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java index 0669e6b3..2aa878a1 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java @@ -56,7 +56,8 @@ public boolean accept(String type) { @Override public void writeReport(String templateSource, String target, Map dataTables) { - // initialize velocity runtime engine + // (re)initialize velocity runtime engine + Velocity.reset(); Velocity.init(); // set up the context diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 4267b3e8..1ba1c311 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1551,6 +1551,7 @@ from a corporate server or even a public service available on the internet. == Release Notes Changes in 1.6.0:: +* https://github.com/devonfw/solicitor/issues/146: Fixed the bug which prevented already defined velocity macro with same name to be redefined in different template. Changes in 1.5.0:: * https://github.com/devonfw/solicitor/issues/6: Fixed the bug by allowing multiple `NormalizedLicense` entries with same id per `ApplicationComponent` if the declared license differs. This allows to assign multiple licenses of same type (e.g. MIT) to a component and also will allow multiple "UNKNOWN" licenses to be reported for the same component. Note that as a side effect additional and unexpected `NormalizedLicense` entries might now be created. This might be caused from multiple `LicenseAssignment*.xls` rules firing for different `RawLicense` entries in the same `ApplicationComponent` and resulting in identical `NormalizedLicense` id. In this case it is necessary to restrict those different rules to only fire for specific `RawLicense` entries. From 78547e2c2bef94ff2a376e79f2c8db99a0db3c1f Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 13 Dec 2022 14:52:23 +0100 Subject: [PATCH 022/139] Introduce sourceRepoUrl as new property to ApplicationComponent (#148) Co-authored-by: Mahmoud Alkam --- .../componentinfo/ComponentInfo.java | 11 ++- .../ComponentInfoInventoryProcessor.java | 4 + .../scancode/ScancodeComponentInfo.java | 31 +++++- .../scancode/ScancodeFileAdapter.java | 2 +- .../model/ModelImporterExporter.java | 7 ++ .../solicitor/model/impl/ModelRootImpl.java | 2 +- .../inventory/ApplicationComponentImpl.java | 24 ++++- .../model/inventory/ApplicationComponent.java | 20 +++- .../NpmLicenseCheckerReader.java | 4 +- .../NpmLicenseCrawlerReader.java | 17 ++-- .../tools/solicitor/reader/ort/OrtReader.java | 37 ++++---- .../solicitor/reader/yarn/YarnReader.java | 5 +- ...ncomponents_with_noncommerciallicenses.sql | 2 + ...th_noncommerciallicenses_with_licenses.sql | 30 +++--- ...alizedlicenses_aggregated_applications.sql | 3 + .../tools/solicitor/templates/Attributions.vm | 8 +- .../model/ModelImporterExporterTest.java | 27 +++++- .../NpmLicenseCheckerReaderTests.java | 18 +++- .../reader/yarn/YarnReaderTests.java | 29 ++++++ .../resources/models/model_version_4.json | 93 ++++++++++++++++++ .../resources/models/model_version_5.json | 94 +++++++++++++++++++ documentation/master-solicitor.asciidoc | 2 + 22 files changed, 397 insertions(+), 73 deletions(-) create mode 100644 core/src/test/resources/models/model_version_4.json create mode 100644 core/src/test/resources/models/model_version_5.json diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java index 486744ef..2994ad03 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java @@ -31,10 +31,17 @@ public interface ComponentInfo { String getNoticeFilePath(); /** - * Gets the url to the license text + * Gets the url of the projects homepage, * - * @return url to the license text + * @return url to the projects homepage */ String getUrl(); + /** + * Gets the url of the source code repository. + * + * @return sourceRepoUrl to the license text + */ + String getSourceRepoUrl(); + } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index ae50de19..13911101 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -110,6 +110,10 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { if (componentInfo.getUrl() != null) { ac.setOssHomepage(componentInfo.getUrl()); } + // check whether Source Reop Url is included in input file or not + if (componentInfo.getSourceRepoUrl() != null) { + ac.setSourceRepoUrl(componentInfo.getSourceRepoUrl()); + } } else { // no ComponentInfos info found for ac } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index 97a966db..3640461e 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -166,6 +166,8 @@ public void setLicenseFileScore(double licenseFileScore) { private String url; + private String sourceRepoUrl; + private double minLicensefilePercentage; private double minLicenseScore; @@ -282,7 +284,7 @@ public void addNoticeFilePath(String path, double score) { } /** - * Gets the path to the notice file (if any) + * Gets the path to the notice file (if any). * * @return path to the notice file */ @@ -293,9 +295,9 @@ public String getNoticeFilePath() { } /** - * Gets the url to the license text + * Gets the url of the projects homepage. * - * @return url to the license text + * @return url to the projects homepage */ @Override public String getUrl() { @@ -304,7 +306,7 @@ public String getUrl() { } /** - * Sets the url to the license text + * Sets the url of the projects homepage. * * @param url new value of {@link #getUrl()}. */ @@ -312,4 +314,25 @@ public void setUrl(String url) { this.url = url; } + + /** + * Gets the url of the source code repository. + * + * @return sourceRepoUrl to the license text + */ + @Override + public String getSourceRepoUrl() { + + return this.sourceRepoUrl; + } + + /** + * Sets the url of the source code repository. + * + * @param sourceRepoUrl new value of {@link #getSourceRepoUrl()}. + */ + public void setSourceRepoUrl(String sourceRepoUrl) { + + this.sourceRepoUrl = sourceRepoUrl; + } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java index 91067ad9..f9ef78e8 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java @@ -265,7 +265,7 @@ private void applyCurations(String packageUrl, ScancodeComponentInfo componentSc } if (curations.get("url") != null) { String url = curations.get("url").asText(); - oneComponent.setUrl(url); + oneComponent.setSourceRepoUrl(url); } if (curations.get("licenses") != null) { oneComponent.clearLicenses(); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java index 5da088c5..0cb6c43d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java @@ -38,6 +38,8 @@ public class ModelImporterExporter { private static final int LOWEST_VERSION_WITH_PACKAGE_URL = 4; + private static final int LOWEST_VERSION_WITH_SOURCE_REPO_URL = 5; + @Autowired private ModelFactory modelFactory; @@ -109,6 +111,10 @@ private void readApplicationComponents(Application application, JsonNode applica String usagePattern = applicationComponentNode.get("usagePattern").asText(null); boolean ossModified = applicationComponentNode.get("ossModified").asBoolean(); String ossHomepage = applicationComponentNode.get("ossHomepage").asText(null); + String sourceRepoUrl = null; + if (readModelVersion >= LOWEST_VERSION_WITH_SOURCE_REPO_URL) { + sourceRepoUrl = applicationComponentNode.get("sourceRepoUrl").asText(null); + } String groupId = applicationComponentNode.get("groupId").asText(null); String artifactId = applicationComponentNode.get("artifactId").asText(null); String version = applicationComponentNode.get("version").asText(null); @@ -129,6 +135,7 @@ private void readApplicationComponents(Application application, JsonNode applica applicationComponent.setUsagePattern(UsagePattern.valueOf(usagePattern)); applicationComponent.setOssModified(ossModified); applicationComponent.setOssHomepage(ossHomepage); + applicationComponent.setSourceRepoUrl(sourceRepoUrl); applicationComponent.setGroupId(groupId); applicationComponent.setArtifactId(artifactId); applicationComponent.setVersion(version); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelRootImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelRootImpl.java index 75b4f4b6..b6eb82db 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelRootImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelRootImpl.java @@ -13,7 +13,7 @@ */ public class ModelRootImpl extends AbstractModelObject implements ModelRoot { - private static final int DEFAULT_MODEL_VERSION = 4; + private static final int DEFAULT_MODEL_VERSION = 5; private String executionTime; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java index 37251053..afa9ae0f 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java @@ -35,6 +35,8 @@ public class ApplicationComponentImpl extends AbstractModelObject implements App private String ossHomepage; + private String sourceRepoUrl; + private String noticeFileUrl; private String groupId; @@ -102,8 +104,8 @@ public String getArtifactId() { public String[] getDataElements() { return new String[] { this.groupId, this.artifactId, this.version, getRepoType(), getPackageUrl(), getOssHomepage(), - getNoticeFileUrl(), getNoticeFileContent(), getUsagePattern().toString(), isOssModified() ? "true" : "false", - getCopyrights() }; + getSourceRepoUrl(), getNoticeFileUrl(), getNoticeFileContent(), getUsagePattern().toString(), + isOssModified() ? "true" : "false", getCopyrights() }; } /** {@inheritDoc} */ @@ -117,8 +119,8 @@ public String getGroupId() { @Override public String[] getHeadElements() { - return new String[] { "groupId", "artifactId", "version", "repoType", "packageUrl", "ossHomepage", "noticeFileUrl", - "noticeFileContent", "usagePattern", "ossModified", "copyrights" }; + return new String[] { "groupId", "artifactId", "version", "repoType", "packageUrl", "ossHomepage", "sourceRepoUrl", + "noticeFileUrl", "noticeFileContent", "usagePattern", "ossModified", "copyrights" }; } /** {@inheritDoc} */ @@ -135,6 +137,13 @@ public String getOssHomepage() { return this.ossHomepage; } + /** {@inheritDoc} */ + @Override + public String getSourceRepoUrl() { + + return this.sourceRepoUrl; + } + /** {@inheritDoc} */ @Override public String getNoticeFileUrl() { @@ -236,6 +245,13 @@ public void setOssHomepage(String ossHomepage) { this.ossHomepage = ossHomepage; } + /** {@inheritDoc} */ + @Override + public void setSourceRepoUrl(String sourceRepoUrl) { + + this.sourceRepoUrl = sourceRepoUrl; + } + /** {@inheritDoc} */ @Override public void setNoticeFileUrl(String noticeFileUrl) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java index 5c016024..18e99ff6 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java @@ -64,6 +64,13 @@ public interface ApplicationComponent { */ String getOssHomepage(); + /** + * This method gets the field sourceRepoUrl. + * + * @return the field sourceRepoUrl + */ + String getSourceRepoUrl(); + /** * This method gets the field noticeFileUrl. * @@ -154,11 +161,18 @@ public interface ApplicationComponent { void setOssHomepage(String ossHomepage); /** - * This method sets the field noticeFileUrl. + * This method sets the field ossHomepage. + * + * @param ossHomepage the new value of the field ossHomepage + */ + void setSourceRepoUrl(String ossHomepage); + + /** + * This method sets the field sourceRepoUrl. * - * @param noticeFileUrl the new value of the field noticeFileUrl + * @param sourceRepoUrl the new value of the field sourceRepoUrl */ - void setNoticeFileUrl(String noticeFileUrl); + void setNoticeFileUrl(String sourceRepoUrl); /** * This method sets the field ossModified. diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java index 7cb1ec95..c5cc002d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java @@ -65,9 +65,6 @@ public void readInventory(String type, String sourceUrl, Application application String licenseFile = (String) attributes.get("licenseFile"); String licenseUrl = estimateLicenseUrl(repo, path, licenseFile); String homePage = (String) attributes.get("url"); - if (homePage == null || homePage.isEmpty()) { - homePage = repo; - } Object lic = attributes.get("licenses"); List licenseList; @@ -98,6 +95,7 @@ public void readInventory(String type, String sourceUrl, Application application appComponent.setUsagePattern(usagePattern); appComponent.setGroupId(""); appComponent.setOssHomepage(homePage); + appComponent.setSourceRepoUrl(repo); appComponent.setRepoType(repoType); appComponent.setPackageUrl(PackageURLHelper.fromNpmPackageNameWithVersion(name).toString()); if (licenseList.isEmpty()) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java index 4b303d7d..9a95ee31 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java @@ -31,10 +31,10 @@ * A {@link Reader} which reads data produced by the NPM * License Crawler. *

- * This reader requires a specific version of license-checker which is not released in the official npm repositories but is only - * available via Github. This might result in difficulties in environments which have only limited access to internet resources. - * Additionally, developer dependencies cannot be excluded as the --production option seemingly does not work properly. - * Use {@link NpmLicenseCheckerReader} instead. + * This reader requires a specific version of license-checker which is not released in the official npm repositories + * but is only available via Github. This might result in difficulties in environments which have only limited access to + * internet resources. Additionally, developer dependencies cannot be excluded as the --production option seemingly does + * not work properly. Use {@link NpmLicenseCheckerReader} instead. */ @Component public class NpmLicenseCrawlerReader extends AbstractReader implements Reader { @@ -68,9 +68,10 @@ public Set getSupportedTypes() { @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, String repoType, Map configuration) { - this.deprecationChecker.check(true, - "Use of Reader of type '"+ SUPPORTED_TYPE +"' is deprecated, use 'npm-license-checker' instead. See https://github.com/devonfw/solicitor/issues/125"); - + + this.deprecationChecker.check(true, "Use of Reader of type '" + SUPPORTED_TYPE + + "' is deprecated, use 'npm-license-checker' instead. See https://github.com/devonfw/solicitor/issues/125"); + if (SUPPORTED_TYPE_DEPRECATED.equals(type)) { this.deprecationChecker.check(true, "Use of type 'npm' is deprecated. Change type in config to '" + SUPPORTED_TYPE + "'. See https://github.com/devonfw/solicitor/issues/62"); @@ -98,7 +99,7 @@ public void readInventory(String type, String sourceUrl, Application application appComponent.setVersion(module[module.length - 1]); appComponent.setUsagePattern(usagePattern); appComponent.setGroupId(""); - appComponent.setOssHomepage(record.get(2)); + appComponent.setSourceRepoUrl(record.get(2)); appComponent.setRepoType(repoType); appComponent.setPackageUrl(PackageURLHelper.fromNpmPackageNameWithVersion(record.get(0)).toString()); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java index 45aea82a..db15b3a3 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java @@ -22,7 +22,8 @@ import com.fasterxml.jackson.databind.SerializationFeature; /** - * A {@link Reader} which reads data generated by the ORT-Analyzer component. + * A {@link Reader} which reads data generated by the + * ORT-Analyzer component. */ @Component public class OrtReader extends AbstractReader implements Reader { @@ -54,40 +55,38 @@ public void readInventory(String type, String sourceUrl, Application application Map l = mapper.readValue(this.inputStreamFactory.createInputStreamFor(sourceUrl), Map.class); Map analyzer = (Map) l.get("analyzer"); Map result = (Map) analyzer.get("result"); - List packages = (List) result.get("packages"); + List packages = (List) result.get("packages"); for (int i = 0; i < packages.size(); i++) { - Map iterator = (Map) packages.get(i); - Map singlePackage = (Map) iterator.get("package"); - String id = (String) singlePackage.get("id"); - Map vcsProcessed = (Map) singlePackage.get("vcs_processed"); - String repo = (String) vcsProcessed.get("url"); - String pURL = (String) singlePackage.get("purl"); - - String homePage = (String) singlePackage.get("homepage_url"); - if (homePage == null || homePage.isEmpty()) { - homePage = repo; - } + Map iterator = (Map) packages.get(i); + Map singlePackage = (Map) iterator.get("package"); + String id = (String) singlePackage.get("id"); + Map vcsProcessed = (Map) singlePackage.get("vcs_processed"); + String repo = (String) vcsProcessed.get("url"); + String pURL = (String) singlePackage.get("purl"); + + String homePage = (String) singlePackage.get("homepage_url"); ApplicationComponent appComponent = getModelFactory().newApplicationComponent(); appComponent.setApplication(application); componentCount++; - + // resolve id into groupId/artifactId/version/repoType String[] resolvedId = id.split(":"); String trueRepoType = resolvedId[0]; String groupId = resolvedId[1]; String artifactId = resolvedId[2]; String version = resolvedId[3]; - + appComponent.setGroupId(groupId); appComponent.setArtifactId(artifactId); appComponent.setVersion(version); appComponent.setUsagePattern(usagePattern); appComponent.setOssHomepage(homePage); + appComponent.setSourceRepoUrl(repo); appComponent.setRepoType(trueRepoType); appComponent.setPackageUrl(pURL); - + // manage multiple declared licenses List lic = (List) singlePackage.get("declared_licenses"); if (lic.isEmpty()) { @@ -98,11 +97,11 @@ public void readInventory(String type, String sourceUrl, Application application licenseCount++; addRawLicense(appComponent, cl.toString(), null, sourceUrl); } - } - doLogging(sourceUrl, application, componentCount, licenseCount); + } + doLogging(sourceUrl, application, componentCount, licenseCount); } } catch (IOException e) { throw new SolicitorRuntimeException("Could not read ort license inventory source '" + sourceUrl + "'", e); } - } + } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java index 95128b43..625a7165 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java @@ -78,11 +78,7 @@ public void readInventory(String type, String sourceUrl, Application application if (attributes.size() == 6) { if (attributes.get(4) != null && !attributes.get(4).isEmpty()) { homePage = attributes.get(4); - } else { - homePage = repo; } - } else if (attributes.size() == 5) { - homePage = repo; } ApplicationComponent appComponent = getModelFactory().newApplicationComponent(); @@ -93,6 +89,7 @@ public void readInventory(String type, String sourceUrl, Application application appComponent.setUsagePattern(usagePattern); appComponent.setGroupId(""); appComponent.setOssHomepage(homePage); + appComponent.setSourceRepoUrl(repo); appComponent.setRepoType(repoType); appComponent.setPackageUrl(PackageURLHelper.fromNpmPackageNameAndVersion(name, version).toString()); diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql index 342c69a7..4afb4618 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql @@ -6,6 +6,7 @@ select ac."groupId", ac."artifactId", ac."ossHomepage", + ac."sourceRepoUrl", ac."copyrights" from APPLICATION a, @@ -19,6 +20,7 @@ group by "groupId", "artifactId", "ossHomepage", + "sourceRepoUrl", "copyrights" order by "groupId", diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql index d020df5d..3230f55c 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql @@ -1,31 +1,33 @@ -- SPDX-License-Identifier: Apache-2.0 -- -select - GROUP_CONCAT(DISTINCT a."applicationName" ORDER BY "applicationName" DESC SEPARATOR ', ') as APPS, - GROUP_CONCAT(DISTINCT ac."version" ORDER BY "version" DESC SEPARATOR ', ') as "version" , - ac."groupId", +select + GROUP_CONCAT(DISTINCT a."applicationName" ORDER BY "applicationName" DESC SEPARATOR ', ') as APPS, + GROUP_CONCAT(DISTINCT ac."version" ORDER BY "version" DESC SEPARATOR ', ') as "version" , + ac."groupId", ac."artifactId", ac."ossHomepage", + ac."sourceRepoUrl", ac."copyrights", l."normalizedLicense", l."effectiveNormalizedLicense" -from - APPLICATION a, - APPLICATIONCOMPONENT ac, - NORMALIZEDLICENSE l +from + APPLICATION a, + APPLICATIONCOMPONENT ac, + NORMALIZEDLICENSE l where a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND - l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE') -group by - "groupId", + l."normalizedLicenseType" NOT in ('COMMERCIAL', 'IGNORE') +group by + "groupId", "artifactId", "ossHomepage", + "sourceRepoUrl", "copyrights", "normalizedLicense", "effectiveNormalizedLicense" -order by - "groupId", - "artifactId", +order by + "groupId", + "artifactId", "version", "normalizedLicense" diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql index 8f6a0da8..010451a7 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql @@ -17,6 +17,7 @@ select "repoType", "packageUrl", "ossHomepage", + "sourceRepoUrl", "usagePattern", "ossModified", "declaredLicense", @@ -50,6 +51,7 @@ from ( ac."repoType", ac."packageUrl", ac."ossHomepage", + ac."sourceRepoUrl", ac."usagePattern", ac."ossModified", l."declaredLicense", @@ -85,6 +87,7 @@ from ( "repoType", "packageUrl", "ossHomepage", + "sourceRepoUrl", "usagePattern", "ossModified", "declaredLicense", diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm index 3986dcc4..dfd07f9b 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm @@ -60,7 +60,7 @@ The following table lists all third party Open Source Components that may be con Name Version - Source-Core-Repository + Source Code Repository Licenses Copyrights (or Authors/Contributors) @@ -75,7 +75,7 @@ The following table lists all third party Open Source Components that may be con #set( $apps = $license.APPS ) $license.artifactId ## application component name $license.version ## application component version - $license.ossHomepage + $license.sourceRepoUrl #foreach($ac in $NONCOMMERCIALCOMPONENTS_LICENSES ) #if( $aid == $ac.artifactId && $gid == $ac.groupId && $ver == $ac.version ) @@ -104,9 +104,9 @@ This section lists all license texts that might be applicable for the third part

  • the unique license text (or text referencing the license)
  • the licenses given or referenced in this text (see note below)
  • the components for which this text applies
  • - + Note that not every license given or referenced in below texts necessarily applies in the -context of the distribution of this product: See the table in Component / +context of the distribution of this product: See the table in Component / License overview for licenses which might not apply due to selected dual-/multi-licensing options.

    diff --git a/core/src/test/java/com/devonfw/tools/solicitor/model/ModelImporterExporterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/model/ModelImporterExporterTest.java index c9378215..3287099b 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/model/ModelImporterExporterTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/model/ModelImporterExporterTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; + import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; /** @@ -25,10 +26,10 @@ public class ModelImporterExporterTest { @Test public void testLoadModelVersion1() { - SolicitorRuntimeException thrown = Assertions.assertThrows(SolicitorRuntimeException.class, () ->{ - this.mie.loadModel("src/test/resources/models/model_version_1.json"); - }); - + SolicitorRuntimeException thrown = Assertions.assertThrows(SolicitorRuntimeException.class, () -> { + this.mie.loadModel("src/test/resources/models/model_version_1.json"); + }); + } /** @@ -49,6 +50,24 @@ public void testLoadModelVersion3() { this.mie.loadModel("src/test/resources/models/model_version_3.json"); } + /** + * Test method for {@link com.devonfw.tools.solicitor.model.ModelImporterExporter#loadModel(java.lang.String)}. + */ + @Test + public void testLoadModelVersion4() { + + this.mie.loadModel("src/test/resources/models/model_version_4.json"); + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.model.ModelImporterExporter#loadModel(java.lang.String)}. + */ + @Test + public void testLoadModelVersion5() { + + this.mie.loadModel("src/test/resources/models/model_version_5.json"); + } + /** * Test method for * {@link com.devonfw.tools.solicitor.model.ModelImporterExporter#saveModel(com.devonfw.tools.solicitor.model.ModelRoot, java.lang.String)}. diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java index e96269b6..39d61ec1 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java @@ -106,12 +106,26 @@ public void testHomepageWhichIsGiven() { } @Test - public void testHomepageFromRepo() { + public void testHomepageNotGiven() { List lapc = this.application.getApplicationComponents(); boolean found = false; for (ApplicationComponent ap : lapc) { - if (ap.getArtifactId().equals("foo-bar") && ap.getOssHomepage().equals("https://github.com/nobody/foo-bar")) { + if (ap.getArtifactId().equals("foo-bar") && ap.getOssHomepage() == null) { + found = true; + break; + } + } + assertTrue(found); + } + + @Test + public void testSourceRepoFromRepo() { + + List lapc = this.application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("foo") && ap.getSourceRepoUrl().equals("https://github.com/somebody/foo")) { found = true; break; } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/yarn/YarnReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/yarn/YarnReaderTests.java index 5b1b6d80..83998d28 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/yarn/YarnReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/yarn/YarnReaderTests.java @@ -89,6 +89,35 @@ public void testHomepageWhichIsGiven() { assertTrue(found); } + @Test + public void testSourceRepoUrlWhichIsGiven() { + + List lapc = this.application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("@test/testing") + && ap.getSourceRepoUrl().equals("https://yarnpkg.com/package/@test/testing")) { + found = true; + break; + } + } + assertTrue(found); + } + + @Test + public void testHomepageWhichIsNotGiven() { + + List lapc = this.application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("@test/testing") && ap.getOssHomepage().equals("")) { + found = true; + break; + } + } + assertTrue(found); + } + @Test public void testLicenseUrlWhichIsGiven() { diff --git a/core/src/test/resources/models/model_version_4.json b/core/src/test/resources/models/model_version_4.json new file mode 100644 index 00000000..8a7f7e72 --- /dev/null +++ b/core/src/test/resources/models/model_version_4.json @@ -0,0 +1,93 @@ +{ + "executionTime" : "Fri Oct 01 18:38:31 CEST 2021", + "modelVersion" : 4, + "solicitorVersion" : "1.3.0-SNAPSHOT", + "solicitorGitHash" : "a507f22", + "solicitorBuilddate" : "2021-10-01 18:34:12 +0200", + "extensionArtifactId" : "cap-solicitor-extension", + "extensionVersion" : "1.3.0-RC1", + "extensionGitHash" : "043a961", + "extensionBuilddate" : "2021-09-13 22:24:45", + "engagement" : { + "engagementName" : "Some Engagement", + "engagementType" : "INTERN", + "clientName" : "none", + "goToMarketModel" : "LICENSE", + "contractAllowsOss" : true, + "ossPolicyFollowed" : true, + "customerProvidesOss" : false, + "applications" : [ { + "name" : "Some Application", + "releaseId" : "1.2.3-SNAPSHOT", + "releaseDate" : "-UNDEFINED-", + "sourceRepo" : "https://point/to/your/repo.git", + "programmingEcosystem" : "Java8", + "applicationComponents" : [ { + "usagePattern" : "DYNAMIC_LINKING", + "ossModified" : false, + "ossHomepage" : null, + "groupId" : "ch.qos.logback", + "artifactId" : "logback-classic", + "version" : "1.2.3", + "repoType" : "maven", + "packageUrl": "pkg:maven/ch.qos.logback/logback-classic@1.2.3", + "normalizedLicenses" : [ { + "declaredLicense" : "Eclipse Public License - v 1.0", + "licenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "normalizedLicenseType" : "OSS-SPDX", + "normalizedLicense" : "EPL-1.0", + "normalizedLicenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "effectiveNormalizedLicenseType" : "IGNORE", + "effectiveNormalizedLicense" : "Ignore", + "effectiveNormalizedLicenseUrl" : null, + "legalPreApproved" : "N/A", + "copyLeft" : "N/A", + "licenseCompliance" : "N/A", + "licenseRefUrl" : null, + "includeLicense" : "no", + "includeSource" : "no", + "reviewedForRelease" : null, + "comments" : null, + "legalApproved" : "N/A", + "legalComments" : null, + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'\r\n+ Rule Group: LicenseNameMappingDefaults; RuleId: 57; Matching: declaredLicense==Eclipse Public License - v 1.0; Setting: normalizedLicenseType=OSS-SPDX, normalizedLicense=EPL-1.0, normalizedLicenseUrl=http://www.eclipse.org/legal/epl-v10.html (taking data from input)\r\n+ Rule Group: MultiLicenseSelection; RuleId: 6; Matching: groupId==ch.qos.logback, normalizedLicense==EPL-1.0; Setting: effectiveNormalizedLicenseType=IGNORE (multilicensing: ignore this, prefer LGPL-2.1), effectiveNormalizedLicense=Ignore\r\n+ Rule Group: LegalPreEvaluation; RuleId: 29; Matching: effectiveNormalizedLicenseType==IGNORE; Setting: legalPreApproved=N/A, copyLeft=N/A, licenseCompliance=N/A, includeLicense=no, includeSource=no\r\n+ Rule Group: LegalEvaluation; RuleId: 1; Matching: effectiveNormalizedLicenseType==IGNORE; Setting: legalApproved=N/A", + "guessedLicenseUrl" : null, + "guessedLicenseUrlAuditInfo" : null + }, { + "declaredLicense" : "GNU Lesser General Public License", + "licenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "normalizedLicenseType" : "OSS-SPDX", + "normalizedLicense" : "LGPL-2.1", + "normalizedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "effectiveNormalizedLicenseType" : "OSS-SPDX", + "effectiveNormalizedLicense" : "LGPL-2.1", + "effectiveNormalizedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "legalPreApproved" : "no", + "copyLeft" : "weak", + "licenseCompliance" : "check legal", + "licenseRefUrl" : "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt", + "includeLicense" : "yes", + "includeSource" : "yes", + "reviewedForRelease" : null, + "comments" : null, + "legalApproved" : "Conditional", + "legalComments" : "OK, in case of dynamic linking.", + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'\r\n+ Rule Group: LicenseNameMappingDefaults; RuleId: 101; Matching: licenseUrl==REGEX:https?://www.gnu.org/licenses/old-licenses/lgpl-2.1.*; Setting: normalizedLicenseType=OSS-SPDX, normalizedLicense=LGPL-2.1, normalizedLicenseUrl=http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html (taking data from input)\r\n+ Rule Group: LicenseSelection; RuleId: DEFAULT; Matching: -default-; Setting: effectiveNormalizedLicenseType=OSS-SPDX (taking data from input), effectiveNormalizedLicense=LGPL-2.1 (taking data from input), effectiveNormalizedLicenseUrl=http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html (taking data from input)\r\n+ Rule Group: LegalPreEvaluation; RuleId: 6; Matching: effectiveNormalizedLicenseType==OSS-SPDX, effectiveNormalizedLicense==LGPL-2.1; Setting: legalPreApproved=no, copyLeft=weak, licenseCompliance=check legal, licenseRefUrl=https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt, includeLicense=yes, includeSource=yes\r\n+ Rule Group: LegalEvaluation; RuleId: 14; Matching: usagePattern==DYNAMIC_LINKING, effectiveNormalizedLicenseType==OSS-SPDX, effectiveNormalizedLicense==LGPL-2.1; Setting: legalApproved=Conditional, legalComments=OK, in case of dynamic linking.", + "guessedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "guessedLicenseUrlAuditInfo" : "" + } ], + "rawLicenses" : [ { + "declaredLicense" : "Eclipse Public License - v 1.0", + "licenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'", + "specialHandling" : true + }, { + "declaredLicense" : "GNU Lesser General Public License", + "licenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'", + "specialHandling" : true + } ] + } ] + } ] + } +} \ No newline at end of file diff --git a/core/src/test/resources/models/model_version_5.json b/core/src/test/resources/models/model_version_5.json new file mode 100644 index 00000000..607b0469 --- /dev/null +++ b/core/src/test/resources/models/model_version_5.json @@ -0,0 +1,94 @@ +{ + "executionTime" : "Fri Oct 01 18:38:31 CEST 2021", + "modelVersion" : 5, + "solicitorVersion" : "1.3.0-SNAPSHOT", + "solicitorGitHash" : "a507f22", + "solicitorBuilddate" : "2021-10-01 18:34:12 +0200", + "extensionArtifactId" : "cap-solicitor-extension", + "extensionVersion" : "1.3.0-RC1", + "extensionGitHash" : "043a961", + "extensionBuilddate" : "2021-09-13 22:24:45", + "engagement" : { + "engagementName" : "Some Engagement", + "engagementType" : "INTERN", + "clientName" : "none", + "goToMarketModel" : "LICENSE", + "contractAllowsOss" : true, + "ossPolicyFollowed" : true, + "customerProvidesOss" : false, + "applications" : [ { + "name" : "Some Application", + "releaseId" : "1.2.3-SNAPSHOT", + "releaseDate" : "-UNDEFINED-", + "sourceRepo" : "https://point/to/your/repo.git", + "programmingEcosystem" : "Java8", + "applicationComponents" : [ { + "usagePattern" : "DYNAMIC_LINKING", + "ossModified" : false, + "ossHomepage" : null, + "sourceRepoUrl": "https://github.com/qos-ch/logback", + "groupId" : "ch.qos.logback", + "artifactId" : "logback-classic", + "version" : "1.2.3", + "repoType" : "maven", + "packageUrl": "pkg:maven/ch.qos.logback/logback-classic@1.2.3", + "normalizedLicenses" : [ { + "declaredLicense" : "Eclipse Public License - v 1.0", + "licenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "normalizedLicenseType" : "OSS-SPDX", + "normalizedLicense" : "EPL-1.0", + "normalizedLicenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "effectiveNormalizedLicenseType" : "IGNORE", + "effectiveNormalizedLicense" : "Ignore", + "effectiveNormalizedLicenseUrl" : null, + "legalPreApproved" : "N/A", + "copyLeft" : "N/A", + "licenseCompliance" : "N/A", + "licenseRefUrl" : null, + "includeLicense" : "no", + "includeSource" : "no", + "reviewedForRelease" : null, + "comments" : null, + "legalApproved" : "N/A", + "legalComments" : null, + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'\r\n+ Rule Group: LicenseNameMappingDefaults; RuleId: 57; Matching: declaredLicense==Eclipse Public License - v 1.0; Setting: normalizedLicenseType=OSS-SPDX, normalizedLicense=EPL-1.0, normalizedLicenseUrl=http://www.eclipse.org/legal/epl-v10.html (taking data from input)\r\n+ Rule Group: MultiLicenseSelection; RuleId: 6; Matching: groupId==ch.qos.logback, normalizedLicense==EPL-1.0; Setting: effectiveNormalizedLicenseType=IGNORE (multilicensing: ignore this, prefer LGPL-2.1), effectiveNormalizedLicense=Ignore\r\n+ Rule Group: LegalPreEvaluation; RuleId: 29; Matching: effectiveNormalizedLicenseType==IGNORE; Setting: legalPreApproved=N/A, copyLeft=N/A, licenseCompliance=N/A, includeLicense=no, includeSource=no\r\n+ Rule Group: LegalEvaluation; RuleId: 1; Matching: effectiveNormalizedLicenseType==IGNORE; Setting: legalApproved=N/A", + "guessedLicenseUrl" : null, + "guessedLicenseUrlAuditInfo" : null + }, { + "declaredLicense" : "GNU Lesser General Public License", + "licenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "normalizedLicenseType" : "OSS-SPDX", + "normalizedLicense" : "LGPL-2.1", + "normalizedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "effectiveNormalizedLicenseType" : "OSS-SPDX", + "effectiveNormalizedLicense" : "LGPL-2.1", + "effectiveNormalizedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "legalPreApproved" : "no", + "copyLeft" : "weak", + "licenseCompliance" : "check legal", + "licenseRefUrl" : "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt", + "includeLicense" : "yes", + "includeSource" : "yes", + "reviewedForRelease" : null, + "comments" : null, + "legalApproved" : "Conditional", + "legalComments" : "OK, in case of dynamic linking.", + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'\r\n+ Rule Group: LicenseNameMappingDefaults; RuleId: 101; Matching: licenseUrl==REGEX:https?://www.gnu.org/licenses/old-licenses/lgpl-2.1.*; Setting: normalizedLicenseType=OSS-SPDX, normalizedLicense=LGPL-2.1, normalizedLicenseUrl=http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html (taking data from input)\r\n+ Rule Group: LicenseSelection; RuleId: DEFAULT; Matching: -default-; Setting: effectiveNormalizedLicenseType=OSS-SPDX (taking data from input), effectiveNormalizedLicense=LGPL-2.1 (taking data from input), effectiveNormalizedLicenseUrl=http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html (taking data from input)\r\n+ Rule Group: LegalPreEvaluation; RuleId: 6; Matching: effectiveNormalizedLicenseType==OSS-SPDX, effectiveNormalizedLicense==LGPL-2.1; Setting: legalPreApproved=no, copyLeft=weak, licenseCompliance=check legal, licenseRefUrl=https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt, includeLicense=yes, includeSource=yes\r\n+ Rule Group: LegalEvaluation; RuleId: 14; Matching: usagePattern==DYNAMIC_LINKING, effectiveNormalizedLicenseType==OSS-SPDX, effectiveNormalizedLicense==LGPL-2.1; Setting: legalApproved=Conditional, legalComments=OK, in case of dynamic linking.", + "guessedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "guessedLicenseUrlAuditInfo" : "" + } ], + "rawLicenses" : [ { + "declaredLicense" : "Eclipse Public License - v 1.0", + "licenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'", + "specialHandling" : true + }, { + "declaredLicense" : "GNU Lesser General Public License", + "licenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'", + "specialHandling" : true + } ] + } ] + } ] + } +} \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 1ba1c311..d028fa66 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -144,6 +144,7 @@ The internal business data model consists of 6 entities: | usagePattern | UsagePattern | possible values: DYNAMIC_LINKING, STATIC_LINKING, STANDALONE_PRODUCT | ossModified | boolean | is the OSS modified? | ossHomepage | String | URL of the OSS homepage +| sourceRepoUrl | String | URL of the Source-Code-Repo | groupId | String | component identifier: maven group | artifactId | String | component identifier: maven artifactId | version | String | component identifier: Version @@ -1552,6 +1553,7 @@ from a corporate server or even a public service available on the internet. Changes in 1.6.0:: * https://github.com/devonfw/solicitor/issues/146: Fixed the bug which prevented already defined velocity macro with same name to be redefined in different template. +* https://github.com/devonfw/solicitor/issues/135: Introduce `sourceRepoUrl` as new property in `ApplicationComponent`. Depending on the kind of Reader either `ossHomepage` and/or `sourceRepoUrl` will be filled with data. Changes in 1.5.0:: * https://github.com/devonfw/solicitor/issues/6: Fixed the bug by allowing multiple `NormalizedLicense` entries with same id per `ApplicationComponent` if the declared license differs. This allows to assign multiple licenses of same type (e.g. MIT) to a component and also will allow multiple "UNKNOWN" licenses to be reported for the same component. Note that as a side effect additional and unexpected `NormalizedLicense` entries might now be created. This might be caused from multiple `LicenseAssignment*.xls` rules firing for different `RawLicense` entries in the same `ApplicationComponent` and resulting in identical `NormalizedLicense` id. In this case it is necessary to restrict those different rules to only fire for specific `RawLicense` entries. From bf6ad1b24407b51ee9b53643e6154cbc3b4d807a Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 14 Dec 2022 14:02:30 +0100 Subject: [PATCH 023/139] Name mappings for SPDX-IDs of frequently used licenses (#150) --- .../rules/LicenseNameMappingSample.xls | Bin 77824 -> 86016 bytes documentation/master-solicitor.asciidoc | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls index 22a2abcb6de625cbc30a13d56aa0222876130aac..f6a38ea5fcb99075177f2b2055da96de567ccd4b 100644 GIT binary patch delta 16932 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 delta 15421 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 XVb?#ZDtI2V)Fq#{hqdvH+lu>tdEM<@ diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index d028fa66..9c52320c 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1553,7 +1553,8 @@ from a corporate server or even a public service available on the internet. Changes in 1.6.0:: * https://github.com/devonfw/solicitor/issues/146: Fixed the bug which prevented already defined velocity macro with same name to be redefined in different template. -* https://github.com/devonfw/solicitor/issues/135: Introduce `sourceRepoUrl` as new property in `ApplicationComponent`. Depending on the kind of Reader either `ossHomepage` and/or `sourceRepoUrl` will be filled with data. +* https://github.com/devonfw/solicitor/issues/135: Introduce `sourceRepoUrl` as new property in `ApplicationComponent`. Depending on the kind of Reader either `ossHomepage` and/or `sourceRepoUrl` will be filled with data. +* https://github.com/devonfw/solicitor/issues/149: Added name mappings so that for all SPDX-IDs used in the name mapping the SPDX-ID itself is also recognized and formally mapped. Changes in 1.5.0:: * https://github.com/devonfw/solicitor/issues/6: Fixed the bug by allowing multiple `NormalizedLicense` entries with same id per `ApplicationComponent` if the declared license differs. This allows to assign multiple licenses of same type (e.g. MIT) to a component and also will allow multiple "UNKNOWN" licenses to be reported for the same component. Note that as a side effect additional and unexpected `NormalizedLicense` entries might now be created. This might be caused from multiple `LicenseAssignment*.xls` rules firing for different `RawLicense` entries in the same `ApplicationComponent` and resulting in identical `NormalizedLicense` id. In this case it is necessary to restrict those different rules to only fire for specific `RawLicense` entries. From c482a2c6ecbf8524b375f83fd3cfa90caaf31e53 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 14 Dec 2022 15:33:31 +0100 Subject: [PATCH 024/139] Version 1.6.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index e8cfacc6..34127807 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.6.0-SNAPSHOT + 1.6.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index ae6e8d6f..64adf8bf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.6.0-SNAPSHOT + 1.6.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 6aaf526f..d18f8b55 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.6.0-SNAPSHOT + 1.6.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index f7151c90..2d7ba561 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.6.0-SNAPSHOT + 1.6.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 0f9d290a..27b17ff4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.6.0-SNAPSHOT + 1.6.0 pom Solicitor Aggregator From 1de7cef006e2151837a7a531357b38c1edba6c38 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 15 Dec 2022 10:57:49 +0100 Subject: [PATCH 025/139] Set Version to SNAPSHOT of next minor version --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 34127807..2d263fb5 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.6.0 + 1.7.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 64adf8bf..1325be8b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.6.0 + 1.7.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index d18f8b55..3a4dd15c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.6.0 + 1.7.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 2d7ba561..4807dddc 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.6.0 + 1.7.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 27b17ff4..38c0d170 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.6.0 + 1.7.0-SNAPSHOT pom Solicitor Aggregator From dc4963f18f7d245c2dcdaa0724ebb44e8b1ef64c Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 15 Dec 2022 11:07:52 +0100 Subject: [PATCH 026/139] Use STATIC_LINKING in configuration snippet for yarn reader (#151) --- documentation/master-solicitor.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 9c52320c..e2382cf6 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -706,7 +706,7 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "yarn", "source" : "file:path/to/yarnlicenses.json", - "usagePattern" : "DYNAMIC_LINKING", + "usagePattern" : "STATIC_LINKING", "repoType" : "yarn" } ] ---- From d504720d4ebc4dfa3806ee9793a807a3214bcb7f Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 16 Dec 2022 19:15:38 +0100 Subject: [PATCH 027/139] Enhancements for java extension code (#153) --- core/pom.xml | 4 + .../devonfw/tools/solicitor/Solicitor.java | 6 + .../AbstractSolicitorLifecycleListener.java | 31 +++++ .../lifecycle/LifecycleListenerHolder.java | 56 ++++++++ .../lifecycle/SolicitorLifecycleListener.java | 25 ++++ .../src/main/resources/application.properties | 3 + .../rules/LicenseAssignmentV2Sample.xls | Bin 61952 -> 62976 bytes ...creativecommons_org_publicdomain_zero_1_0_ | 121 ++++++++++++++++++ documentation/master-solicitor.asciidoc | 4 + 9 files changed, 250 insertions(+) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/lifecycle/AbstractSolicitorLifecycleListener.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/lifecycle/LifecycleListenerHolder.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/lifecycle/SolicitorLifecycleListener.java create mode 100644 core/src/test/resources/licenses/http___creativecommons_org_publicdomain_zero_1_0_ diff --git a/core/pom.xml b/core/pom.xml index 1325be8b..3d8b2c78 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -49,6 +49,10 @@ spring-boot-starter-test test
    + + org.springframework.boot + spring-boot-starter-webflux + com.fasterxml.jackson.core jackson-databind diff --git a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java index e84c904b..e7090bdb 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java @@ -20,6 +20,7 @@ import com.devonfw.tools.solicitor.common.ResourceToFileCopier.ResourceGroup; import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; import com.devonfw.tools.solicitor.config.ConfigFactory; +import com.devonfw.tools.solicitor.lifecycle.LifecycleListenerHolder; import com.devonfw.tools.solicitor.model.ModelImporterExporter; import com.devonfw.tools.solicitor.model.ModelRoot; import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; @@ -62,6 +63,9 @@ public class Solicitor { @Autowired private ModelImporterExporter modelImporterExporter; + @Autowired + private LifecycleListenerHolder lifecycleListenerHolder; + private boolean tolerateMissingInput = false; @Value("${solicitor.tolerate-missing-input}") @@ -108,6 +112,7 @@ private void extractConfig(String targetDir) { private void mainProcessing(CommandLineOptions clo) { ModelRoot modelRoot = this.configFactory.createConfig(clo.configUrl); + this.lifecycleListenerHolder.modelRootInitialized(modelRoot); if (clo.load) { LOG.info(LogMessages.LOADING_DATAMODEL.msg(), clo.pathForLoad); modelRoot = this.modelImporterExporter.loadModel(clo.pathForLoad); @@ -126,6 +131,7 @@ private void mainProcessing(CommandLineOptions clo) { oldModelRoot = this.modelImporterExporter.loadModel(clo.pathForDiff); } this.writerFacade.writeResult(modelRoot, oldModelRoot); + this.lifecycleListenerHolder.endOfMainProcessing(modelRoot); } /** diff --git a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/AbstractSolicitorLifecycleListener.java b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/AbstractSolicitorLifecycleListener.java new file mode 100644 index 00000000..6bf7cf55 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/AbstractSolicitorLifecycleListener.java @@ -0,0 +1,31 @@ +package com.devonfw.tools.solicitor.lifecycle; + +import com.devonfw.tools.solicitor.model.ModelRoot; + +/** + * Abstract base implementation of {@link SolicitorLifecycleListener}. Methods do nothing by default and might be + * overridden in concrete implementation beans. + * + */ +public abstract class AbstractSolicitorLifecycleListener implements SolicitorLifecycleListener { + + /** + * The constructor. + */ + public AbstractSolicitorLifecycleListener() { + + } + + @Override + public void modelRootInitialized(ModelRoot modelRoot) { + + // NOOP by default + } + + @Override + public void endOfMainProcessing(ModelRoot modelRoot) { + + // NOOP by default + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/LifecycleListenerHolder.java b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/LifecycleListenerHolder.java new file mode 100644 index 00000000..2803c0d3 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/LifecycleListenerHolder.java @@ -0,0 +1,56 @@ +package com.devonfw.tools.solicitor.lifecycle; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.model.ModelRoot; + +/** + * Holds all beans of type {@link SolicitorLifecycleListener} and calls all of their respective methods. + */ +@Component +public class LifecycleListenerHolder { + + @Autowired(required = false) + private SolicitorLifecycleListener[] listeners; + + /** + * The constructor. + */ + public LifecycleListenerHolder() { + + } + + /** + * Calls {@link SolicitorLifecycleListener#modelRootInitialized(ModelRoot)} of all registered + * {@link SolicitorLifecycleListener}s. + * + * @param modelRoot the model data as existing at the beginning of the main processing + */ + public void modelRootInitialized(ModelRoot modelRoot) { + + if (this.listeners == null) { + return; + } + for (SolicitorLifecycleListener sll : this.listeners) { + sll.modelRootInitialized(modelRoot); + } + } + + /** + * Calls {@link SolicitorLifecycleListener#endOfMainProcessing(ModelRoot)} of all registered + * {@link SolicitorLifecycleListener}s. + * + * @param modelRoot the model data at the end of main processing + */ + public void endOfMainProcessing(ModelRoot modelRoot) { + + if (this.listeners == null) { + return; + } + for (SolicitorLifecycleListener sll : this.listeners) { + sll.endOfMainProcessing(modelRoot); + } + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/SolicitorLifecycleListener.java b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/SolicitorLifecycleListener.java new file mode 100644 index 00000000..0e98718b --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/SolicitorLifecycleListener.java @@ -0,0 +1,25 @@ +package com.devonfw.tools.solicitor.lifecycle; + +import com.devonfw.tools.solicitor.model.ModelRoot; + +/** + * Interface of Beans which will be called at specific points in the Solicitor execution lifecycle. + */ +public interface SolicitorLifecycleListener { + + /** + * Method to be called at the beginning of the main processing after the model root has been initialized (i.e. the + * configuration has been read.). + * + * @param modelRoot the model data as existing at the beginning of the main processing + */ + void modelRootInitialized(ModelRoot modelRoot); + + /** + * Method to be called at the end of main processing. + * + * @param modelRoot the model data as existing at the end of the main processing + */ + void endOfMainProcessing(ModelRoot modelRoot); + +} diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index 8d56c0fb..b30cba44 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -67,6 +67,9 @@ solicitor.tolerate-missing-input=false # and might override properties given here spring.profiles.include=extension +# do not allow spring to start any webserver - even if the components are included in the classpath +spring.main.web-application-type=none + ## Default values of the properties giving information about a possibly active extension # this will be overriden by definitions given in application-extension.properties in the # the extension classpath diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls index 09a8cbde5a5e07ba03f0946f4e9d0aa6424e0298..5cfbbc75b4976924513d3fe0688ae7ec8ff7de06 100644 GIT binary patch delta 1323 zcmZ8hZ%kWN6hG&+g_ib};-Hi;v@FY(C8N?&z%fJ+XqGXTp~<3ta5U`$I^No@En}d8 z;`YfcI=Hed3&vi*IHV4Y;xevyouetaBe!uhQ zo_p@=OWe>U=5(Wa$rJ^Ehv{@W#%1XX+!P^tU_}ygoNy(}dy?;4`4YX_3}pQ&-X38mg5YSp}Li3g;4oZd>FP zWRsVX-GWd7W710ATDkbC^9HPgCb5EW^2l|u`ZJW*QGXey|u8he^$yupx9tplj@`@_*dFx2LZhn=lKRdKc}zJL;qIen_C z3524GQw{o+P)rFpJ40l3)j4;M z04FX0to;UHWSVvDJ+meXTc89YT9ZN$hf;7eS4&}G1*rh22TZ~Iy6g#%5KM5&>@fyd zsO4z>t#tq|?cVvaxJ7~ME6ca;b-TIE7m!fLFmNOrAq?%HfFC+&jve`QkvISyvq{_Dswa1U)KO~3D)TEC$@H+;D5*xdAS;fXe~9Yx;Mx(C_K zj%-8nb_yZqSArt*H(h! z#mNbteTzTa??pVokGwfhQ0gXe0TyYd2eZK_*`SQzBQ1g3HO-@GdSs2mr*$Oqx#1Vq z^z}+v^x9E(02144muM?D=%EolB5j!&h>fhW3}PRzvP@#{6C2m!jKmu04wmDx2yb2W zG!qlgvduzF?7H-X7AO<_gy=Xtz%pJl`ZpGcYU?#`4|Sjcnl$q~diaaUzmS)m`_91Y z&ZUrDyRFU%a@8<_B1<>0a=%D--$r=EN+r%Tt>%$?i{W zl#T%_ns)HY=}QKKXk3Kdy?nAtTv?NBFY%wj{3Bj?PN}F=DMe|1uRP>{0 z@u00xLH#Lc@j?rT77-HI@~6!tEz&G15~R>9JNLCnT+Y4UJ?GqWzWd!9{%8rmwOmyd zdk6r2j*gC23thQ_@e)d_VfR+HZEV}wcCgjbYfHjWzpX(H>ftqUSBf1k(87K%?NoOw zKc9+W^%Yv>>1>&K&97pEt;HU08vi?$uxwU?eNZ{CiLdq{#1MValFT{Q5e-L>ODQ0o zZd&Nn%z&GM|Ii^5dG%PwL^By>a*H$GOpK<_PW=yx(tjhj6Ja{ED1YjD^`wN574x75 zDxeI4kPeffi-WVrdWZGDgmTB6l2E5E3H6y_n~`F6ilrjM&d95~X^X*eQE%)@#ylFD z*%;9sUCI}_AR1?NMq5VVBE4U7Nt`bB8E&a~V=c>i1YqD6%i0RieVe|PUS3eatOCp{ z14_ULb0G!tVJYPC>f<#XX7EuT?=sn!W+DJo0lQ#PVq_aA2zEHBd2B$R{0Am@69JYj zS+rUTEAgD^S=pu8AP$$6E3~a@p9obHq3{P(WV8~+j;f0&dMZ~c)Ul;`+^WLT^t5zJ zQYUFExcNM$Qv}QT4YU5+7YDx4=^C#{tZs@Vf8cXGvbN>0nOTZ-*Qs+dM^_Eg4=V}igedqXu07eSe*+LhE?x~m|2E=!amnY`H4{)$@$hsBqz7+;B zlUw;H>6lBzUGG87W2Uu27kb3Nt!@+}H#)4MxvdMeoPVJl4!uOP`b-)SE^+W~KZ?4$ z!>BnRm&@n#P-s(oDthQt`&9P`Mkya~Y>eOt8X%sxcUbA>gC55-m;#A>+`+ zVX6@U2KPgV;9(d-e>6LW3VI9q{wI1ju*&xv`s$S9PO9_^e>.) Alternative implementations might e.g. get this information from a corporate server or even a public service available on the internet. +===== `com.devonfw.tools.solicitor.lifecycle.SolicitorLifecycleListener` +Spring beans implementing this interface will be called at certain points in the _Solicitor_ processing lifecycle. See the Javadoc for details. Implementations should preferably use `com.devonfw.tools.solicitor.lifecycle.AbstractSolicitorLifecycleListener` as base class which contains NOOP functionality for all methods which might be overridden as required. [appendix] == Release Notes +Changes in 1.7.0:: +* https://github.com/devonfw/solicitor/issues/152: Enhancements for using java code in extensions. Changes in 1.6.0:: * https://github.com/devonfw/solicitor/issues/146: Fixed the bug which prevented already defined velocity macro with same name to be redefined in different template. From 2a3a4bbfb51c3b4a66855226db59819cd261e4ed Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 23 Dec 2022 15:08:59 +0100 Subject: [PATCH 028/139] Version 1.7.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 2d263fb5..e799bb99 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.7.0-SNAPSHOT + 1.7.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 3d8b2c78..b2f393dc 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.7.0-SNAPSHOT + 1.7.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 3a4dd15c..fc7cfdd0 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.7.0-SNAPSHOT + 1.7.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 4807dddc..5b646aea 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.7.0-SNAPSHOT + 1.7.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 38c0d170..c9bbc3d8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.7.0-SNAPSHOT + 1.7.0 pom Solicitor Aggregator From 024655eab0ca5dc1df0c634040b03a411577e16b Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 23 Dec 2022 15:14:20 +0100 Subject: [PATCH 029/139] Set to SNAPSHOT of next minor version --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index e799bb99..1508e48a 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.7.0 + 1.8.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index b2f393dc..f4783136 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.7.0 + 1.8.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index fc7cfdd0..40b34924 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.7.0 + 1.8.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 5b646aea..47f5e9ab 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.7.0 + 1.8.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index c9bbc3d8..2170207f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.7.0 + 1.8.0-SNAPSHOT pom Solicitor Aggregator From 895c6200e2329f09860bedc914398b687d7c5171 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 6 Jan 2023 16:57:21 +0100 Subject: [PATCH 030/139] Fix dependency declaration of solicitor-documentation.pdf artifact (#155) --- core/pom.xml | 1 + documentation/master-solicitor.asciidoc | 3 +++ 2 files changed, 4 insertions(+) diff --git a/core/pom.xml b/core/pom.xml index f4783136..93b7674b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -163,6 +163,7 @@ solicitor-documentation ${project.version} pdf + provided diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 64faea42..56a6813b 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1552,6 +1552,9 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.8.0:: +* https://github.com/devonfw/solicitor/issues/154: Corrected dependency declaration for solicitor-core.jar. + Changes in 1.7.0:: * https://github.com/devonfw/solicitor/issues/152: Enhancements for using java code in extensions. From ae069759e30e9ecb26b40184fdeb0a82d7012b79 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:57:06 +0100 Subject: [PATCH 031/139] Include engagement name as member in SolicitorSetup (#157) --- .../tools/solicitor/SolicitorSetup.java | 22 ++++++++++++ .../tools/solicitor/config/ConfigFactory.java | 1 + documentation/master-solicitor.asciidoc | 35 ++++++++++--------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/SolicitorSetup.java b/core/src/main/java/com/devonfw/tools/solicitor/SolicitorSetup.java index f3d469d7..1bebaeaa 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/SolicitorSetup.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/SolicitorSetup.java @@ -159,12 +159,34 @@ public void setRepoType(String repoType) { } } + private String engagementName; + private List readerSetups = new ArrayList<>(); private List ruleSetups = new ArrayList<>(); private List writerSetups = new ArrayList<>(); + /** + * This method gets the field engagementName. + * + * @return engagementName the engagement name + */ + public String getEngagementName() { + + return this.engagementName; + } + + /** + * This method sets the field engagementName. + * + * @param engagementName the engagement name to set + */ + public void setEngagementName(String engagementName) { + + this.engagementName = engagementName; + } + /** * This method gets the field readerSetups. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java b/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java index 6308d7e1..5c441378 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java @@ -103,6 +103,7 @@ public ModelRoot createConfig(String url) { Map placeHolderMap = createPlaceholderMap(url, sc); LOG.info(LogMessages.CREATING_ENGAGEMENT.msg(), sc.getEngagementName()); + this.solicitorSetup.setEngagementName(sc.getEngagementName()); Engagement engagement = this.modelFactory.newEngagement(sc.getEngagementName(), sc.getEngagementType(), sc.getClientName(), sc.getGoToMarketModel()); engagement.setModelRoot(modelRoot); diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 56a6813b..5726696b 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -646,7 +646,7 @@ In _Solicitor_ the data is read with the following part of the config ==== NPM License Crawler -WARNING: This reader is deprecated and should no longer be used. It requires a specific dependency (license-checker) which is not available on official npm repositories anymore and scans additional developer dependencies. Use <> (with --production option) instead. See <>. +WARNING: This reader is deprecated and should no longer be used. It requires a specific dependency (license-checker) which is not available on official npm repositories anymore and scans additional developer dependencies. Use <> (with --production option) instead. See <>. To install the NPM License Crawler the following command needs to be executed. @@ -754,9 +754,9 @@ Usually, the command to run the analyzer and get extract the result file from a docker run -v C:\\path\\to\\project/:/project ort --info analyze -f JSON -i /project -o /project/ort/analyzer ---- -Note that this command only works for the installation via https://www.docker.com/[Docker] and that we require JSON as the output format. For other installation methods, you need to adjust the command accordingly. +Note that this command only works for the installation via https://www.docker.com/[Docker] and that we require JSON as the output format. For other installation methods, you need to adjust the command accordingly. -It might also be necessary to set up a customized configuration for the analyzer. This can be achieved through a configuration file. The default path for that is the .ort/config/ directory below the current user's home directory. We can place a ort.conf file there, in which we can declare various configurations e.g. allowing dynamic versions in npm components via +It might also be necessary to set up a customized configuration for the analyzer. This can be achieved through a configuration file. The default path for that is the .ort/config/ directory below the current user's home directory. We can place a ort.conf file there, in which we can declare various configurations e.g. allowing dynamic versions in npm components via ---- analyzer { allowDynamicVersions = true @@ -971,7 +971,7 @@ NOTE: With the "V2" version of rules the additional field/condition `origin` was This can be used to fire rules only if the raw license data was obtained from a specific data source. Its primary intention is to distinguish between data obtained via normal readers or from Scancode data. Decision table data for the new data structure is named `LicenseAssignmentV2*.xls/csv`. -The old decision table structure `LicenseAssignment*.xls/csv` is deprecated but for compatibility reasons still supported. +The old decision table structure `LicenseAssignment*.xls/csv` is deprecated but for compatibility reasons still supported. ==== Decision Table: Detecting Licenses from Imported Data With this decision table the license info from the RawLicense is mapped to the NormalizedLicense. This is based on the name and/or URL of the license as imported via the readers. @@ -1339,7 +1339,7 @@ Execute _Solicitor_ in a classic way. As part of the report creation step this w * `output/scancode_PROJECTNAME.sh` (for downloading the sources, also calls `scancodeScan.sh`) * `output/scancodeScan.sh` (for running ScanCode on the downloaded sources) -Scripts will include all ApplicationComponents with exception of those where `normalizedLicenseType` was set to `COMMERCIAL`. +Scripts will include all ApplicationComponents with exception of those where `normalizedLicenseType` was set to `COMMERCIAL`. ==== Download Sources and run Scancode Change to directory `output` and execute `sh scancode_PROJECTNAME.sh`. @@ -1355,7 +1355,7 @@ _Solicitor_ will try to look up ScanCode information from the directory tree in * License information (including URL of license text) as obtained from the Readers is replaced by the license info found by ScanCode * Copyrights are taken from ScanCode results * Info on NOTICE file is taken from the ScanCode results -* If the ScanCode results contain information about a project URL then this is stored as `ossHomepage` +* If the ScanCode results contain information about a project URL then this is stored as `ossHomepage` ==== Output Main target of the additional information obtained from ScanCode is currently the new report `Attributions_PROJECTNAME.html` which lists @@ -1376,12 +1376,12 @@ To define curations you might create a file `output/curations.yaml` containing t [source,yaml] ---- -artifacts: +artifacts: - name: pkg/npm/@somescope/somepackage/1.2.3 <1> url: https://github.com/foo/bar <2> licenses: <3> - - license: MIT <4> - url: https://raw.githubusercontent.com/foo/bar/LICENSE <5> + - license: MIT <4> + url: https://raw.githubusercontent.com/foo/bar/LICENSE <5> copyrights: <6> - (c) 2021 Donald Duck <7> - "(c) 2019 Mickey Mouse " <8> @@ -1391,7 +1391,7 @@ artifacts: . ---- <1> Path of the package information as used in the file tree. Derived from the PackageURL. -<2> URL of the project, will be stored as `ossHomepage`. (Optional: no change if not existing.) +<2> URL of the project, will be stored as `ossHomepage`. (Optional: no change if not existing.) <3> Licenses to set. Optional. If defined then all found licenses will be replaced by the list of licenses given here. <4> SPDX identifier of license. <5> URL pointing to license text. @@ -1408,9 +1408,9 @@ Using the <> it is possible to qualify whether a rul [cols="1,2",options="header"] |==================== |Value of condition Origin | rule applies for ... -|`scancode` | ... licenses obtained from ScanCode information -|`NOT:scancode` | ... licenses obtained from normal Readers -| _(empty)_ | ... in both cases +|`scancode` | ... licenses obtained from ScanCode information +|`NOT:scancode` | ... licenses obtained from normal Readers +| _(empty)_ | ... in both cases |==================== @@ -1541,19 +1541,20 @@ WARNING: Extending Solicitor via Java is an advanced topic. Only the Interfaces be regarded as unstable and might change without notice. For any details on the interfaces see the _Solicitor_ source code and corresponding Javadoc. ==== Extension Interfaces -===== `com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter` +===== `com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter` A spring bean implementing this interface might provide `ComponentInfo`/`LicenseInfo` data for `ApplicationComponents` identified by their packageUrl. (The buildin implementation of this interface is reading such component info from scancode result files from the local file system, see <>.) Alternative implementations might e.g. get this information from a corporate server or even a public service available on the internet. -===== `com.devonfw.tools.solicitor.lifecycle.SolicitorLifecycleListener` +===== `com.devonfw.tools.solicitor.lifecycle.SolicitorLifecycleListener` Spring beans implementing this interface will be called at certain points in the _Solicitor_ processing lifecycle. See the Javadoc for details. Implementations should preferably use `com.devonfw.tools.solicitor.lifecycle.AbstractSolicitorLifecycleListener` as base class which contains NOOP functionality for all methods which might be overridden as required. [appendix] == Release Notes Changes in 1.8.0:: * https://github.com/devonfw/solicitor/issues/154: Corrected dependency declaration for solicitor-core.jar. +* https://github.com/devonfw/solicitor/issues/156: Included engagement name as member in class SolicitorSetup. Changes in 1.7.0:: * https://github.com/devonfw/solicitor/issues/152: Enhancements for using java code in extensions. @@ -1564,7 +1565,7 @@ Changes in 1.6.0:: * https://github.com/devonfw/solicitor/issues/149: Added name mappings so that for all SPDX-IDs used in the name mapping the SPDX-ID itself is also recognized and formally mapped. Changes in 1.5.0:: -* https://github.com/devonfw/solicitor/issues/6: Fixed the bug by allowing multiple `NormalizedLicense` entries with same id per `ApplicationComponent` if the declared license differs. This allows to assign multiple licenses of same type (e.g. MIT) to a component and also will allow multiple "UNKNOWN" licenses to be reported for the same component. Note that as a side effect additional and unexpected `NormalizedLicense` entries might now be created. This might be caused from multiple `LicenseAssignment*.xls` rules firing for different `RawLicense` entries in the same `ApplicationComponent` and resulting in identical `NormalizedLicense` id. In this case it is necessary to restrict those different rules to only fire for specific `RawLicense` entries. +* https://github.com/devonfw/solicitor/issues/6: Fixed the bug by allowing multiple `NormalizedLicense` entries with same id per `ApplicationComponent` if the declared license differs. This allows to assign multiple licenses of same type (e.g. MIT) to a component and also will allow multiple "UNKNOWN" licenses to be reported for the same component. Note that as a side effect additional and unexpected `NormalizedLicense` entries might now be created. This might be caused from multiple `LicenseAssignment*.xls` rules firing for different `RawLicense` entries in the same `ApplicationComponent` and resulting in identical `NormalizedLicense` id. In this case it is necessary to restrict those different rules to only fire for specific `RawLicense` entries. Changes in 1.4.0:: * https://github.com/devonfw/solicitor/issues/141: Improved robustness of report generation in cases where PackageURL can not be determined (e.g. if data originates from CSV reader). @@ -1575,7 +1576,7 @@ from alternative sources. See <>. * https://github.com/devonfw/solicitor/issues/130: Fixed a bug where the guessedLicenseUrl and guessedLicenseUrlAuditInfo fields were not filled correctly in the aggregated inventory. * Added reader for data generated by OSS Review Toolkit (ORT). See <>. * Added support for API changes of new scancode release (v31) https://github.com/nexB/scancode-toolkit/releases/tag/v31.0.1. -* https://github.com/devonfw/solicitor/issues/124: Added documentation of '--production' option for npm-license-checker plugin. +* https://github.com/devonfw/solicitor/issues/124: Added documentation of '--production' option for npm-license-checker plugin. * https://github.com/devonfw/solicitor/issues/125: Deprecated usage of npm-license-crawler. * Stability and data corruption safety for bash scripts of scancode integration. * Initial version of experimental scancode integration. See <>. From 05fb20466897c7fde58394f8baac445e241a5ccc Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 30 Jan 2023 23:29:44 +0100 Subject: [PATCH 032/139] Add com.auth0/java-jwt as dependency (#158). (#159) --- core/pom.xml | 5 +++++ ...rcontent_com_auth0_java_jwt_master_LICENSE | 21 +++++++++++++++++++ documentation/master-solicitor.asciidoc | 1 + 3 files changed, 27 insertions(+) create mode 100644 core/src/test/resources/licenses/http___raw_githubusercontent_com_auth0_java_jwt_master_LICENSE diff --git a/core/pom.xml b/core/pom.xml index 93b7674b..9a6e0eb6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -158,6 +158,11 @@ packageurl-java 1.4.1 + + com.auth0 + java-jwt + 4.2.1 + ${project.groupId} solicitor-documentation diff --git a/core/src/test/resources/licenses/http___raw_githubusercontent_com_auth0_java_jwt_master_LICENSE b/core/src/test/resources/licenses/http___raw_githubusercontent_com_auth0_java_jwt_master_LICENSE new file mode 100644 index 00000000..bcd1854c --- /dev/null +++ b/core/src/test/resources/licenses/http___raw_githubusercontent_com_auth0_java_jwt_master_LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Auth0, Inc. (http://auth0.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 5726696b..f669081f 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1555,6 +1555,7 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.8.0:: * https://github.com/devonfw/solicitor/issues/154: Corrected dependency declaration for solicitor-core.jar. * https://github.com/devonfw/solicitor/issues/156: Included engagement name as member in class SolicitorSetup. +* https://github.com/devonfw/solicitor/issues/158: Include library com.auth0/java-jwt to make it available for extensions. Changes in 1.7.0:: * https://github.com/devonfw/solicitor/issues/152: Enhancements for using java code in extensions. From 59d10dba16ee101eb595c8ac1736283ce34ce83f Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 2 Feb 2023 23:10:00 +0100 Subject: [PATCH 033/139] Include license texts in persisted datamodel json file (#161) --- .../model/ModelImporterExporter.java | 85 +++++- .../model/impl/AbstractModelObject.java | 47 ++- .../model/impl/ModelFactoryImpl.java | 17 +- .../solicitor/model/impl/ModelRootImpl.java | 23 +- .../tools/solicitor/model/impl/TextPool.java | 27 ++ .../solicitor/model/impl/TextPoolImpl.java | 78 +++++ .../impl/inventory/NormalizedLicenseImpl.java | 281 +++++++++++++++++- .../model/inventory/NormalizedLicense.java | 36 +++ .../model/impl/TextPoolImplTest.java | 49 +++ documentation/master-solicitor.asciidoc | 3 +- 10 files changed, 603 insertions(+), 43 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/model/impl/TextPoolImplTest.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java index 0cb6c43d..32d0c836 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java @@ -13,6 +13,14 @@ import com.devonfw.tools.solicitor.common.IOHelper; import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.model.impl.ModelFactoryImpl; +import com.devonfw.tools.solicitor.model.impl.ModelRootImpl; +import com.devonfw.tools.solicitor.model.impl.TextPool; +import com.devonfw.tools.solicitor.model.impl.inventory.ApplicationComponentImpl; +import com.devonfw.tools.solicitor.model.impl.inventory.NormalizedLicenseImpl; +import com.devonfw.tools.solicitor.model.impl.inventory.RawLicenseImpl; +import com.devonfw.tools.solicitor.model.impl.masterdata.ApplicationImpl; +import com.devonfw.tools.solicitor.model.impl.masterdata.EngagementImpl; import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; import com.devonfw.tools.solicitor.model.inventory.NormalizedLicense; import com.devonfw.tools.solicitor.model.inventory.RawLicense; @@ -40,8 +48,10 @@ public class ModelImporterExporter { private static final int LOWEST_VERSION_WITH_SOURCE_REPO_URL = 5; + private static final int LOWEST_VERSION_WITH_TEXT_POOL = 6; + @Autowired - private ModelFactory modelFactory; + private ModelFactoryImpl modelFactory; /** * Loads the data model from a file. @@ -49,13 +59,13 @@ public class ModelImporterExporter { * @param filename the name of the file to load from. * @return the root object of the loaded data model */ - public ModelRoot loadModel(String filename) { + public ModelRootImpl loadModel(String filename) { ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); try { JsonNode root = objectMapper.readTree(new File(filename)); int readModelVersion = root.get("modelVersion").asInt(); - ModelRoot modelRoot = this.modelFactory.newModelRoot(); + ModelRootImpl modelRoot = this.modelFactory.newModelRoot(); checkModelVersion(readModelVersion, modelRoot); String executionTime = root.get("executionTime").asText(); String solicitorVersion = root.get("solicitorVersion").asText(); @@ -66,6 +76,10 @@ public ModelRoot loadModel(String filename) { String extensionGitHash = root.get("extensionGitHash").asText(); String extensionBuilddate = root.get("extensionBuilddate").asText(); JsonNode engagementNode = root.get("engagement"); + JsonNode textPoolNode = null; + if (readModelVersion >= LOWEST_VERSION_WITH_TEXT_POOL) { + textPoolNode = root.get("textPool"); + } modelRoot.setExecutionTime(executionTime); modelRoot.setSolicitorVersion(solicitorVersion); modelRoot.setSolicitorGitHash(solicitorGitHash); @@ -75,6 +89,11 @@ public ModelRoot loadModel(String filename) { modelRoot.setExtensionGitHash(extensionGitHash); modelRoot.setExtensionBuilddate(extensionBuilddate); readEngagement(modelRoot, engagementNode, readModelVersion); + readTextPool(modelRoot, textPoolNode, readModelVersion); + if (readModelVersion < LOWEST_VERSION_WITH_TEXT_POOL) { + // previous versions do not contain license texts, so complete the data now + modelRoot.completeData(); + } return modelRoot; } catch (IOException e) { throw new SolicitorRuntimeException("Could not load internal data model from file '" + filename + "'", e); @@ -88,7 +107,7 @@ public ModelRoot loadModel(String filename) { * @param readModelVersion the model version of the model to be loaded * @param currentModelRoot the root object of the current (target) model */ - private void checkModelVersion(int readModelVersion, ModelRoot currentModelRoot) { + private void checkModelVersion(int readModelVersion, ModelRootImpl currentModelRoot) { if (readModelVersion < LOWEST_SUPPORTED_MODEL_VERSION || readModelVersion > currentModelRoot.getModelVersion()) { throw new SolicitorRuntimeException( @@ -104,7 +123,7 @@ private void checkModelVersion(int readModelVersion, ModelRoot currentModelRoot) * @param applicationComponentsNode the relevant part of the parse JSON model * @param readModelVersion the model version of the model to be read */ - private void readApplicationComponents(Application application, JsonNode applicationComponentsNode, + private void readApplicationComponents(ApplicationImpl application, JsonNode applicationComponentsNode, int readModelVersion) { for (JsonNode applicationComponentNode : applicationComponentsNode) { @@ -130,7 +149,7 @@ private void readApplicationComponents(Application application, JsonNode applica JsonNode normalizedLicensesNode = applicationComponentNode.get("normalizedLicenses"); JsonNode rawLicensesNode = applicationComponentNode.get("rawLicenses"); - ApplicationComponent applicationComponent = this.modelFactory.newApplicationComponent(); + ApplicationComponentImpl applicationComponent = this.modelFactory.newApplicationComponent(); applicationComponent.setApplication(application); applicationComponent.setUsagePattern(UsagePattern.valueOf(usagePattern)); applicationComponent.setOssModified(ossModified); @@ -158,7 +177,7 @@ private void readApplicationComponents(Application application, JsonNode applica * @param applicationsNode the relevant part of the parsed JSON model * @param readModelVersion the model version of the model to be read */ - private void readApplications(Engagement engagement, JsonNode applicationsNode, int readModelVersion) { + private void readApplications(EngagementImpl engagement, JsonNode applicationsNode, int readModelVersion) { for (JsonNode applicationNode : applicationsNode) { String name = applicationNode.get("name").asText(null); @@ -167,7 +186,7 @@ private void readApplications(Engagement engagement, JsonNode applicationsNode, String sourceRepo = applicationNode.get("sourceRepo").asText(null); String programmingEcosystem = applicationNode.get("programmingEcosystem").asText(null); JsonNode applicationComponentsNode = applicationNode.get("applicationComponents"); - Application application = this.modelFactory.newApplication(name, releaseId, releaseDate, sourceRepo, + ApplicationImpl application = this.modelFactory.newApplication(name, releaseId, releaseDate, sourceRepo, programmingEcosystem); application.setEngagement(engagement); readApplicationComponents(application, applicationComponentsNode, readModelVersion); @@ -183,7 +202,7 @@ private void readApplications(Engagement engagement, JsonNode applicationsNode, * @param engagementNode the relevant part of the parsed JSON model * @param readModelVersion the model version of the model to be read */ - private void readEngagement(ModelRoot modelRoot, JsonNode engagementNode, int readModelVersion) { + private void readEngagement(ModelRootImpl modelRoot, JsonNode engagementNode, int readModelVersion) { String engagementName = engagementNode.get("engagementName").asText(null); String engagementType = engagementNode.get("engagementType").asText(null); @@ -194,7 +213,7 @@ private void readEngagement(ModelRoot modelRoot, JsonNode engagementNode, int re boolean customerProvidesOss = engagementNode.get("customerProvidesOss").asBoolean(); JsonNode applicationsNode = engagementNode.get("applications"); - Engagement engagement = this.modelFactory.newEngagement(engagementName, EngagementType.valueOf(engagementType), + EngagementImpl engagement = this.modelFactory.newEngagement(engagementName, EngagementType.valueOf(engagementType), clientName, GoToMarketModel.valueOf(goToMarketModel)); engagement.setModelRoot(modelRoot); engagement.setContractAllowsOss(contractAllowsOss); @@ -210,7 +229,7 @@ private void readEngagement(ModelRoot modelRoot, JsonNode engagementNode, int re * @param normalizedLicensesNode the relevant part of the parsed JSON model * @param readModelVersion the model version of the model to be read */ - private void readNormalizedLicenses(ApplicationComponent applicationComponent, JsonNode normalizedLicensesNode, + private void readNormalizedLicenses(ApplicationComponentImpl applicationComponent, JsonNode normalizedLicensesNode, int readModelVersion) { for (JsonNode normalizedLicenseNode : normalizedLicensesNode) { @@ -239,8 +258,21 @@ private void readNormalizedLicenses(ApplicationComponent applicationComponent, J guessedLicenseUrl = normalizedLicenseNode.get("guessedLicenseUrl").asText(null); guessedLicenseUrlAuditInfo = normalizedLicenseNode.get("guessedLicenseUrlAuditInfo").asText(null); } + String effectiveNormalizedLicenseContentKey = null; + String declaredLicenseContentKey = null; + String licenseRefContentKey = null; + String normalizedLicenseContentKey = null; + String guessedLicenseContentKey = null; + if (readModelVersion >= LOWEST_VERSION_WITH_TEXT_POOL) { + effectiveNormalizedLicenseContentKey = normalizedLicenseNode.get("effectiveNormalizedLicenseContentKey") + .asText(null); + declaredLicenseContentKey = normalizedLicenseNode.get("declaredLicenseContentKey").asText(null); + licenseRefContentKey = normalizedLicenseNode.get("licenseRefContentKey").asText(null); + normalizedLicenseContentKey = normalizedLicenseNode.get("normalizedLicenseContentKey").asText(null); + guessedLicenseContentKey = normalizedLicenseNode.get("guessedLicenseContentKey").asText(null); + } - NormalizedLicense normalizedLicense = this.modelFactory.newNormalizedLicense(); + NormalizedLicenseImpl normalizedLicense = this.modelFactory.newNormalizedLicense(); normalizedLicense.setApplicationComponent(applicationComponent); normalizedLicense.setDeclaredLicense(declaredLicense); normalizedLicense.setLicenseUrl(licenseUrl); @@ -263,6 +295,11 @@ private void readNormalizedLicenses(ApplicationComponent applicationComponent, J normalizedLicense.setTrace(trace); normalizedLicense.setGuessedLicenseUrl(guessedLicenseUrl); normalizedLicense.setGuessedLicenseUrlAuditInfo(guessedLicenseUrlAuditInfo); + normalizedLicense.setEffectiveNormalizedLicenseContentKey(effectiveNormalizedLicenseContentKey); + normalizedLicense.setDeclaredLicenseContentKey(declaredLicenseContentKey); + normalizedLicense.setLicenseRefContentKey(licenseRefContentKey); + normalizedLicense.setNormalizedLicenseContentKey(normalizedLicenseContentKey); + normalizedLicense.setGuessedLicenseContentKey(guessedLicenseContentKey); } } @@ -273,7 +310,7 @@ private void readNormalizedLicenses(ApplicationComponent applicationComponent, J * @param rawLicensesNode the relevant part of the parsed JSON model * @param readModelVersion the model version of the model to be read */ - private void readRawLicenses(ApplicationComponent applicationComponent, JsonNode rawLicensesNode, + private void readRawLicenses(ApplicationComponentImpl applicationComponent, JsonNode rawLicensesNode, int readModelVersion) { for (JsonNode rawLicenseNode : rawLicensesNode) { @@ -284,7 +321,7 @@ private void readRawLicenses(ApplicationComponent applicationComponent, JsonNode String origin = originNode != null ? originNode.asText(null) : null; boolean specialHandling = rawLicenseNode.get("specialHandling").asBoolean(); - RawLicense rawLicense = this.modelFactory.newRawLicense(); + RawLicenseImpl rawLicense = this.modelFactory.newRawLicense(); rawLicense.setApplicationComponent(applicationComponent); rawLicense.setDeclaredLicense(declaredLicense); rawLicense.setLicenseUrl(licenseUrl); @@ -295,6 +332,26 @@ private void readRawLicenses(ApplicationComponent applicationComponent, JsonNode } } + /** + * Read the {@link TextPool} from the JSON data structure. + * + * @param modelRoot the root object of the data model to which the {@link TextPool} should be added + * @param textPoolNode the relevant part of the parsed JSON model + * @param readModelVersion the model version of the model to be read + */ + private void readTextPool(ModelRootImpl modelRoot, JsonNode textPoolNode, int readModelVersion) { + + if (readModelVersion < LOWEST_VERSION_WITH_TEXT_POOL) { + return; + } + TextPool textPool = modelRoot.getTextPool(); + JsonNode dataMapNode = textPoolNode.get("dataMap"); + for (JsonNode singleEntryValue : dataMapNode) { + // only store values; keys will be reconstructed based on values + textPool.store(singleEntryValue.asText()); + } + } + /** * Saves the model to a file. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/AbstractModelObject.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/AbstractModelObject.java index beee3dc4..d43d3690 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/AbstractModelObject.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/AbstractModelObject.java @@ -39,7 +39,7 @@ public static String[] concatRow(String[] first, String[] second) { public AbstractModelObject() { synchronized (integerFormat) { - id = integerFormat.format(idSingleton++); + this.id = integerFormat.format(idSingleton++); } } @@ -64,7 +64,7 @@ protected Object doGetParent() { /** * Gets an array of Strings which are the names of the datafields contained in this object. - * + * * @return the column name array */ @JsonIgnore @@ -72,13 +72,13 @@ protected Object doGetParent() { /** * Gets the id of the model object. - * + * * @return the id */ @JsonIgnore public String getId() { - return id; + return this.id; } /** @@ -92,4 +92,43 @@ public final AbstractModelObject getParent() { return (AbstractModelObject) doGetParent(); } + /** + * Gets the TextPool for effectively storing license and other texts. Recursively walks up the object tree up to the + * {@link ModelRootImpl}. + * + * @return the text pool of the model + */ + @JsonIgnore + protected TextPool getEffectiveTextPool() { + + if (getParent() == null) { + throw new IllegalStateException("Method might not be called unless object is linked to model tree"); + } + return getParent().getEffectiveTextPool(); + + } + + /** + * Stores a text in the text pool. + * + * @param text the text to store + * @return the associated key + * @see TextPool#store(String) + */ + protected String storeTextInPool(String text) { + + return getEffectiveTextPool().store(text); + } + + /** + * Retrieves a text from the text pool + * + * @param key the key of the text + * @return the associated text + * @see TextPool#retrieve(String) + */ + protected String retrieveTextFromPool(String key) { + + return getEffectiveTextPool().retrieve(key); + } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java index 5210fd51..e69f9e20 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelFactoryImpl.java @@ -28,7 +28,6 @@ import com.devonfw.tools.solicitor.model.inventory.NormalizedLicense; import com.devonfw.tools.solicitor.model.inventory.RawLicense; import com.devonfw.tools.solicitor.model.masterdata.Application; -import com.devonfw.tools.solicitor.model.masterdata.Engagement; import com.devonfw.tools.solicitor.model.masterdata.EngagementType; import com.devonfw.tools.solicitor.model.masterdata.GoToMarketModel; @@ -80,7 +79,7 @@ public Collection getAllModelObjects(ModelRoot modelRoot) { /** {@inheritDoc} */ @Override - public Application newApplication(String name, String releaseId, String releaseDate, String sourceRepo, + public ApplicationImpl newApplication(String name, String releaseId, String releaseDate, String sourceRepo, String programmingEcosystem) { return new ApplicationImpl(name, releaseId, releaseDate, sourceRepo, programmingEcosystem); @@ -88,7 +87,7 @@ public Application newApplication(String name, String releaseId, String releaseD /** {@inheritDoc} */ @Override - public ApplicationComponent newApplicationComponent() { + public ApplicationComponentImpl newApplicationComponent() { ApplicationComponentImpl result = new ApplicationComponentImpl(); result.setLicenseContentProvider(this.licenseContentProvider); @@ -98,7 +97,7 @@ public ApplicationComponent newApplicationComponent() { /** {@inheritDoc} */ @Override - public Engagement newEngagement(String engagementName, EngagementType engagementType, String clientName, + public EngagementImpl newEngagement(String engagementName, EngagementType engagementType, String clientName, GoToMarketModel goToMarketModel) { return new EngagementImpl(engagementName, engagementType, clientName, goToMarketModel); @@ -106,9 +105,9 @@ public Engagement newEngagement(String engagementName, EngagementType engagement /** {@inheritDoc} */ @Override - public ModelRoot newModelRoot() { + public ModelRootImpl newModelRoot() { - ModelRoot modelRoot = new ModelRootImpl(); + ModelRootImpl modelRoot = new ModelRootImpl(); modelRoot.setSolicitorVersion(this.solicitorVersion.getVersion()); modelRoot.setSolicitorGitHash(this.solicitorVersion.getGithash()); modelRoot.setSolicitorBuilddate(this.solicitorVersion.getBuilddate()); @@ -121,7 +120,7 @@ public ModelRoot newModelRoot() { /** {@inheritDoc} */ @Override - public NormalizedLicense newNormalizedLicense() { + public NormalizedLicenseImpl newNormalizedLicense() { NormalizedLicenseImpl result = new NormalizedLicenseImpl(); result.setLicenseContentProvider(this.licenseContentProvider); @@ -131,7 +130,7 @@ public NormalizedLicense newNormalizedLicense() { /** {@inheritDoc} */ @Override - public NormalizedLicense newNormalizedLicense(RawLicense rawLicense) { + public NormalizedLicenseImpl newNormalizedLicense(RawLicense rawLicense) { NormalizedLicenseImpl result = new NormalizedLicenseImpl(rawLicense); result.setLicenseContentProvider(this.licenseContentProvider); @@ -141,7 +140,7 @@ public NormalizedLicense newNormalizedLicense(RawLicense rawLicense) { /** {@inheritDoc} */ @Override - public RawLicense newRawLicense() { + public RawLicenseImpl newRawLicense() { return new RawLicenseImpl(); } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelRootImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelRootImpl.java index b6eb82db..3222b416 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelRootImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/ModelRootImpl.java @@ -13,7 +13,7 @@ */ public class ModelRootImpl extends AbstractModelObject implements ModelRoot { - private static final int DEFAULT_MODEL_VERSION = 5; + private static final int DEFAULT_MODEL_VERSION = 6; private String executionTime; @@ -35,6 +35,8 @@ public class ModelRootImpl extends AbstractModelObject implements ModelRoot { private Engagement engagement; + private TextPool textPool; + /** * Constructor. */ @@ -43,6 +45,8 @@ public ModelRootImpl() { super(); this.modelVersion = DEFAULT_MODEL_VERSION; this.executionTime = (new Date()).toString(); + this.textPool = new TextPoolImpl(); + } /** {@inheritDoc} */ @@ -202,6 +206,23 @@ public void setExtensionBuilddate(String extensionBuilddate) { this.extensionBuilddate = extensionBuilddate; } + /** + * @return textPool + */ + @Override + public TextPool getEffectiveTextPool() { + + return this.textPool; + } + + /** + * @return textPool + */ + public TextPool getTextPool() { + + return this.textPool; + } + @Override public void completeData() { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java new file mode 100644 index 00000000..b997d4d6 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java @@ -0,0 +1,27 @@ +package com.devonfw.tools.solicitor.model.impl; + +import java.util.NoSuchElementException; + +/** + * Holds texts in a pool (effectively a map) and avoids storing the same text multiple times. This can be used to + * minimize storage requirements when persisting a large amount of (possibly duplicated) texts. + */ +public interface TextPool { + + /** + * Store a text in the pool. + * + * @param text the text to store in the pool; might be null + * @return the key of the text in the pool + */ + String store(String text); + + /** + * Retrieves a text from the pool + * + * @param key the key of the text + * @return the stored text; might be null if a null value was stored in the pool + * @throws NoSuchElementException if nothing is stored under the given key + */ + String retrieve(String key); +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java new file mode 100644 index 00000000..4fca32e6 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java @@ -0,0 +1,78 @@ +package com.devonfw.tools.solicitor.model.impl; + +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.TreeMap; + +import org.apache.commons.codec.digest.DigestUtils; + +/** + * Implementation of a {@link TextPool}. + * + */ +public class TextPoolImpl implements TextPool { + + /** The key used for null values. */ + private static final String NULL_KEY = "NULL_KEY"; + + private Map dataMap; + + /** + * @return dataMap + */ + public Map getDataMap() { + + return this.dataMap; + } + + /** + * @param dataMap new value of {@link #getDataMap}. + */ + public void setDataMap(Map dataMap) { + + this.dataMap = dataMap; + } + + /** + * The constructor. + */ + public TextPoolImpl() { + + this.dataMap = new TreeMap<>(); + + } + + @Override + public String store(String text) { + + // special handling of null (null values never get stored in the map) + if (text == null) { + return NULL_KEY; + } + + String key = DigestUtils.sha256Hex(text); + if (!this.dataMap.containsKey(key)) { + // we ignore the possibility of hash collisions (same key, different text); the probability of any additional line + // of code introducing a bug is much higher than the likelihood of a hash collision + this.dataMap.put(key, text); + } + return key; + } + + @Override + public String retrieve(String key) { + + if (key == null) { + throw new NullPointerException("Key for pool must not be null"); + } + if (NULL_KEY.equals(key)) { + return null; + } + String result = this.dataMap.get(key); + if (result == null) { + throw new NoSuchElementException("No data in text pool for key '" + key + "'"); + } + return result; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java index 5d39ab09..21113fa8 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java @@ -28,18 +28,24 @@ public class NormalizedLicenseImpl extends AbstractModelObject implements Normal private String licenseUrl; + private String declaredLicenseContentKey; + private String normalizedLicenseType; private String normalizedLicense; private String normalizedLicenseUrl; + private String normalizedLicenseContentKey; + private String effectiveNormalizedLicenseType; private String effectiveNormalizedLicense; private String effectiveNormalizedLicenseUrl; // really needed? + private String effectiveNormalizedLicenseContentKey; + private String legalPreApproved; private String copyLeft; @@ -48,6 +54,8 @@ public class NormalizedLicenseImpl extends AbstractModelObject implements Normal private String licenseRefUrl; + private String licenseRefContentKey; + private String includeLicense; private String includeSource; @@ -64,6 +72,8 @@ public class NormalizedLicenseImpl extends AbstractModelObject implements Normal private String guessedLicenseUrl; + private String guessedLicenseContentKey; + private String guessedLicenseUrlAuditInfo; private ApplicationComponent applicationComponent; @@ -146,8 +156,17 @@ public String getDeclaredLicense() { @JsonIgnore public String getDeclaredLicenseContent() { - return LicenseTextHelper - .replaceLongHtmlContent(this.licenseContentProvider.getContentForUri(this.licenseUrl).getContent()); + return LicenseTextHelper.replaceLongHtmlContent(retrieveTextFromPool(this.declaredLicenseContentKey)); + } + + /** + * Gets the text pool key of the {@link #getDeclaredLicenseContent()}. + * + * @return the key + */ + public String getDeclaredLicenseContentKey() { + + return this.declaredLicenseContentKey; } /** {@inheritDoc} */ @@ -162,8 +181,17 @@ public String getEffectiveNormalizedLicense() { @JsonIgnore public String getEffectiveNormalizedLicenseContent() { - return LicenseTextHelper.replaceLongHtmlContent( - this.licenseContentProvider.getContentForUri(this.effectiveNormalizedLicenseUrl).getContent()); + return LicenseTextHelper.replaceLongHtmlContent(retrieveTextFromPool(this.effectiveNormalizedLicenseContentKey)); + } + + /** + * Gets the text pool key of the {@link #getEffectiveNormalizedLicenseContent()}. + * + * @return the key + */ + public String getEffectiveNormalizedLicenseContentKey() { + + return this.effectiveNormalizedLicenseContentKey; } /** {@inheritDoc} */ @@ -250,8 +278,17 @@ public ContentProvider getLicenseContentProvider() { @JsonIgnore public String getLicenseRefContent() { - return LicenseTextHelper - .replaceLongHtmlContent(this.licenseContentProvider.getContentForUri(this.licenseRefUrl).getContent()); + return LicenseTextHelper.replaceLongHtmlContent(retrieveTextFromPool(this.licenseRefContentKey)); + } + + /** + * Gets the text pool key of the {@link #getLicenseRefContent()}. + * + * @return the key + */ + public String getLicenseRefContentKey() { + + return this.licenseRefContentKey; } /** {@inheritDoc} */ @@ -294,8 +331,17 @@ public String getNormalizedLicenseUrl() { @JsonIgnore public String getNormalizedLicenseContent() { - return LicenseTextHelper - .replaceLongHtmlContent(this.licenseContentProvider.getContentForUri(this.normalizedLicenseUrl).getContent()); + return LicenseTextHelper.replaceLongHtmlContent(retrieveTextFromPool(this.normalizedLicenseContentKey)); + } + + /** + * Gets the text pool key of the {@link #getNormalizedLicenseContent()}. + * + * @return the key + */ + public String getNormalizedLicenseContentKey() { + + return this.normalizedLicenseContentKey; } /** {@inheritDoc} */ @@ -338,8 +384,17 @@ public String getGuessedLicenseUrlAuditInfo() { @JsonIgnore public String getGuessedLicenseContent() { - return LicenseTextHelper - .replaceLongHtmlContent(this.licenseContentProvider.getContentForUri(this.guessedLicenseUrl).getContent()); + return LicenseTextHelper.replaceLongHtmlContent(retrieveTextFromPool(this.guessedLicenseContentKey)); + } + + /** + * Gets the text pool key of the {@link #getGuessedLicenseContent()}. + * + * @return the key + */ + public String getGuessedLicenseContentKey() { + + return this.guessedLicenseContentKey; } /** {@inheritDoc} */ @@ -374,6 +429,27 @@ public void setDeclaredLicense(String declaredLicense) { this.declaredLicense = declaredLicense; } + /** + * Sets the declaredLicenseContent. + * + * @param declaredLicenseContent the content to set + */ + @Override + public void setDeclaredLicenseContent(String declaredLicenseContent) { + + this.declaredLicenseContentKey = storeTextInPool(declaredLicenseContent); + } + + /** + * Sets the text pool key of the {@link #getDeclaredLicenseContent()}. + * + * @param declaredLicenseContentKey the key + */ + public void setDeclaredLicenseContentKey(String declaredLicenseContentKey) { + + this.declaredLicenseContentKey = declaredLicenseContentKey; + } + /** {@inheritDoc} */ @Override public void setEffectiveNormalizedLicense(String effectiveNormalizedLicense) { @@ -388,6 +464,27 @@ public void setEffectiveNormalizedLicenseType(String effectiveNormalizedLicenseT this.effectiveNormalizedLicenseType = effectiveNormalizedLicenseType; } + /** + * Sets the effectiveNormalizedLicenseContent. + * + * @param effectiveNormalizedLicenseContent the content to set + */ + @Override + public void setEffectiveNormalizedLicenseContent(String effectiveNormalizedLicenseContent) { + + this.effectiveNormalizedLicenseContentKey = storeTextInPool(effectiveNormalizedLicenseContent); + } + + /** + * Sets the text pool key of the {@link #getEffectiveNormalizedLicenseContent()}. + * + * @param effectiveNormalizedLicenseContentKey the key + */ + public void setEffectiveNormalizedLicenseContentKey(String effectiveNormalizedLicenseContentKey) { + + this.effectiveNormalizedLicenseContentKey = effectiveNormalizedLicenseContentKey; + } + /** {@inheritDoc} */ @Override public void setEffectiveNormalizedLicenseUrl(String effectiveNormalizedLicenseUrl) { @@ -464,6 +561,27 @@ public void setLicenseRefUrl(String licenseRefUrl) { this.licenseRefUrl = licenseRefUrl; } + /** + * Sets the licenseRefContent. + * + * @param licenseRefContent the content to set + */ + @Override + public void setLicenseRefContent(String licenseRefContent) { + + this.licenseRefContentKey = storeTextInPool(licenseRefContent); + } + + /** + * Sets the text pool key of the {@link #getLicenseRefContent()}. + * + * @param licenseRefContentKey the key + */ + public void setLicenseRefContentKey(String licenseRefContentKey) { + + this.licenseRefContentKey = licenseRefContentKey; + } + /** {@inheritDoc} */ @Override public void setLicenseUrl(String licenseUrl) { @@ -492,6 +610,27 @@ public void setNormalizedLicenseUrl(String normalizedLicenseUrl) { this.normalizedLicenseUrl = normalizedLicenseUrl; } + /** + * Sets the normalizedLicenseContent. + * + * @param normalizedLicenseContent the content to set + */ + @Override + public void setNormalizedLicenseContent(String normalizedLicenseContent) { + + this.normalizedLicenseContentKey = storeTextInPool(normalizedLicenseContent); + } + + /** + * Sets the text pool key of the {@link #getNormalizedLicenseContent()}. + * + * @param normalizedLicenseContentKey the key + */ + public void setNormalizedLicenseContentKey(String normalizedLicenseContentKey) { + + this.normalizedLicenseContentKey = normalizedLicenseContentKey; + } + /** {@inheritDoc} */ @Override public void setReviewedForRelease(String reviewedForRelease) { @@ -514,6 +653,27 @@ public void setGuessedLicenseUrl(String guessedLicenseUrl) { } + /** + * Sets the guessedLicenseContent. + * + * @param guessedLicenseContent the content to set + */ + @Override + public void setGuessedLicenseContent(String guessedLicenseContent) { + + this.guessedLicenseContentKey = storeTextInPool(guessedLicenseContent); + } + + /** + * Sets the text pool key of the {@link #getGuessedLicenseContent()}. + * + * @param guessedLicenseContentKey the key + */ + public void setGuessedLicenseContentKey(String guessedLicenseContentKey) { + + this.guessedLicenseContentKey = guessedLicenseContentKey; + } + /** {@inheritDoc} */ @Override public void setGuessedLicenseUrlAuditInfo(String guessedLicenseUrlAuditInfo) { @@ -525,11 +685,104 @@ public void setGuessedLicenseUrlAuditInfo(String guessedLicenseUrlAuditInfo) { @Override public void completeData() { - this.guessedLicenseUrl = this.licenseUrlGuesser.getContentForUri(this.effectiveNormalizedLicenseUrl) - .getGuessedUrl(); - this.guessedLicenseUrlAuditInfo = this.licenseUrlGuesser.getContentForUri(this.effectiveNormalizedLicenseUrl) - .getAuditInfo(); + // following methods try to fill some data fields if they do not yet contain data + possiblyGuessLicenseUrl(); + possiblyFillGuessedLicenseContent(); + possiblyFillDeclaredLicenseContent(); + possiblyFillNormalizedLicenseContent(); + possiblyFillEffectiveNormalizedLicenseContent(); + possiblyFillLicenseRefContent(); } + /** + * If the {@link #guessedLicenseUrl} is not yet set it will be tried to guess it. This includes also setting the + * {@link #guessedLicenseUrlAuditInfo}. + */ + private void possiblyGuessLicenseUrl() { + + // execute license guessing based on effectziveNormalizedLicensUrl + if (this.guessedLicenseUrl == null) { + GuessedLicenseUrlContent guessed = this.licenseUrlGuesser.getContentForUri(this.effectiveNormalizedLicenseUrl); + this.guessedLicenseUrl = guessed.getGuessedUrl(); + this.guessedLicenseUrlAuditInfo = guessed.getAuditInfo(); + } + } + + /** + * If the {@link #guessedLicenseContentKey} is not yet set (i.e. the content is not yet set) it will be attempted to + * fetch the content via the {@link #licenseContentProvider} and store it. + */ + private void possiblyFillGuessedLicenseContent() { + + if (this.guessedLicenseContentKey == null) { + setGuessedLicenseContent(this.licenseContentProvider.getContentForUri(this.guessedLicenseUrl).getContent()); + } + } + + /** + * If the {@link #declaredLicenseContentKey} is not yet set(i.e. the content is not set yet) it will be attempted to + * fetch the content via the {@link #licenseContentProvider} and store it. + */ + private void possiblyFillDeclaredLicenseContent() { + + if (this.declaredLicenseContentKey == null) { + setDeclaredLicenseContent(this.licenseContentProvider.getContentForUri(this.licenseUrl).getContent()); + } + } + + /** + * If the {@link #normalizedLicenseContentKey} is not yet set (i.e. the content is not set yet) it will be attempted + * to fill the content. This is tried via two ways: + *
      + *
    • if {@link #normalizedLicenseUrl} equals {@link #licenseUrl} the {@link #getDeclaredLicenseContent()} will be + * taken
    • + *
    • otherwise the content will be fetched via the {@link #licenseContentProvider}
    • + *
    + */ + private void possiblyFillNormalizedLicenseContent() { + + if (this.normalizedLicenseContentKey == null) { + if (this.normalizedLicenseUrl != null && this.normalizedLicenseUrl.equals(this.licenseUrl)) { + setNormalizedLicenseContent(getDeclaredLicenseContent()); + } else { + setNormalizedLicenseContent( + this.licenseContentProvider.getContentForUri(this.normalizedLicenseUrl).getContent()); + } + } + } + + /** + * If the {@link #effectiveNormalizedLicenseContentKey} is not yet set (i.e. the content is not set yet) it will be + * attempted to fill the content. This is tried via two ways: + *
      + *
    • if {@link #effectiveNormalizedLicenseUrl} equals {@link #normalizedLicenseUrl} the + * {@link #getNormalizedLicenseContent()} will be taken
    • + *
    • otherwise the content will be fetched via the {@link #licenseContentProvider}
    • + *
    + */ + private void possiblyFillEffectiveNormalizedLicenseContent() { + + if (this.effectiveNormalizedLicenseContentKey == null) { + if (this.effectiveNormalizedLicenseUrl != null + && this.effectiveNormalizedLicenseUrl.equals(this.normalizedLicenseUrl)) { + setEffectiveNormalizedLicenseContent(getNormalizedLicenseContent()); + } else { + setEffectiveNormalizedLicenseContent( + this.licenseContentProvider.getContentForUri(this.effectiveNormalizedLicenseUrl).getContent()); + } + } + } + + /** + * If the {@link #licenseRefContentKey} is not yet set(i.e. the content is not set yet) it will be attempted to fetch + * the content via the {@link #licenseContentProvider} and store it. + */ + private void possiblyFillLicenseRefContent() { + + if (this.licenseRefContentKey == null) { + setLicenseRefContent(this.licenseContentProvider.getContentForUri(this.licenseRefUrl).getContent()); + } + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java index 73d53ec9..c54cd27b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/NormalizedLicense.java @@ -223,6 +223,13 @@ public interface NormalizedLicense { */ void setDeclaredLicense(String declaredLicense); + /** + * This method sets the field declaredLicenseContent. + * + * @param declaredLicenseContent the content to set + */ + void setDeclaredLicenseContent(String declaredLicenseContent); + /** * This method sets the field effectiveNormalizedLicense. * @@ -237,6 +244,13 @@ public interface NormalizedLicense { */ void setEffectiveNormalizedLicenseType(String effectiveNormalizedLicenseType); + /** + * Sets the effectiveNormalizedLicenseContent. + * + * @param effectiveNormalizedLicenseContent the content to set + */ + void setEffectiveNormalizedLicenseContent(String effectiveNormalizedLicenseContent); + /** * This method sets the field effectiveNormalizedLicenseUrl. * @@ -293,6 +307,13 @@ public interface NormalizedLicense { */ void setLicenseRefUrl(String licenseRefUrl); + /** + * This method sets the field licenseRefContent. + * + * @param licenseRefContent the content to set + */ + void setLicenseRefContent(String licenseRefContent); + /** * This method sets the field licenseUrl. * @@ -321,6 +342,13 @@ public interface NormalizedLicense { */ void setNormalizedLicenseUrl(String normalizedLicenseUrl); + /** + * This method sets the field normalizedLicenseContent. + * + * @param normalizedLicenseContent the content to set + */ + void setNormalizedLicenseContent(String normalizedLicenseContent); + /** * This method sets the field reviewedForRelease. * @@ -342,6 +370,13 @@ public interface NormalizedLicense { */ void setGuessedLicenseUrl(String guessedLicenseUrl); + /** + * This method sets the field guessedLicenseContent + * + * @param guessedLicenseContent the content to set + */ + void setGuessedLicenseContent(String guessedLicenseContent); + /** * This method sets the field guessedLicenseUrlAuditInfo * @@ -353,4 +388,5 @@ public interface NormalizedLicense { * Complete the data of this object by setting members which are derived from other members. */ public void completeData(); + } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/model/impl/TextPoolImplTest.java b/core/src/test/java/com/devonfw/tools/solicitor/model/impl/TextPoolImplTest.java new file mode 100644 index 00000000..fb341221 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/model/impl/TextPoolImplTest.java @@ -0,0 +1,49 @@ +package com.devonfw.tools.solicitor.model.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.NoSuchElementException; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link TextPoolImpl}. + */ +class TextPoolImplTest { + + @Test + void testStoreAndRetrieveNull() { + + TextPool pool = new TextPoolImpl(); + assertEquals("NULL_KEY", pool.store(null)); + + assertNull(pool.retrieve("NULL_KEY")); + } + + @Test + void testThrowExceptionForUnknownKey() { + + TextPool pool = new TextPoolImpl(); + assertThrows(NoSuchElementException.class, () -> pool.retrieve("foo")); + } + + @Test + void testThrowExceptionForNullKey() { + + TextPool pool = new TextPoolImpl(); + assertThrows(NullPointerException.class, () -> pool.retrieve(null)); + } + + @Test + void testStoreAndRetrieveData() { + + TextPool pool = new TextPoolImpl(); + String keyAbc = pool.store("abc"); + String keyDef = pool.store("def"); + assertEquals("abc", pool.retrieve(keyAbc)); + assertEquals("def", pool.retrieve(keyDef)); + } + +} diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index f669081f..29934727 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1555,7 +1555,8 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.8.0:: * https://github.com/devonfw/solicitor/issues/154: Corrected dependency declaration for solicitor-core.jar. * https://github.com/devonfw/solicitor/issues/156: Included engagement name as member in class SolicitorSetup. -* https://github.com/devonfw/solicitor/issues/158: Include library com.auth0/java-jwt to make it available for extensions. +* https://github.com/devonfw/solicitor/issues/158: Include library com.auth0/java-jwt to make it available for extensions. +* https://github.com/devonfw/solicitor/issues/160: License texts are now included in the data model json. Changes in 1.7.0:: * https://github.com/devonfw/solicitor/issues/152: Enhancements for using java code in extensions. From 4062f87dc2ef8151677513c8daede742939e6eb3 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 3 Feb 2023 00:25:17 +0100 Subject: [PATCH 034/139] Include notice file content and license texts in ComponentInfo interface (#163) --- .../componentinfo/ComponentInfo.java | 9 ++- .../ComponentInfoInventoryProcessor.java | 15 +++-- .../solicitor/componentinfo/LicenseInfo.java | 4 ++ .../scancode/ScancodeComponentInfo.java | 61 ++++++++++++++++--- .../scancode/ScancodeFileAdapter.java | 17 +++++- .../tools/solicitor/model/impl/TextPool.java | 5 ++ .../solicitor/model/impl/TextPoolImpl.java | 3 - .../inventory/ApplicationComponentImpl.java | 38 +++++++++++- .../impl/inventory/NormalizedLicenseImpl.java | 1 + .../model/impl/inventory/RawLicenseImpl.java | 38 ++++++++++++ .../model/inventory/ApplicationComponent.java | 19 ++++-- .../solicitor/model/inventory/RawLicense.java | 14 +++++ documentation/master-solicitor.asciidoc | 2 + 13 files changed, 199 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java index 2994ad03..84d9f77b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java @@ -30,12 +30,19 @@ public interface ComponentInfo { */ String getNoticeFilePath(); + /** + * Gets the contents of the notice file (if any) + * + * @return contents of the notice file + */ + String getNoticeFileContent(); + /** * Gets the url of the projects homepage, * * @return url to the projects homepage */ - String getUrl(); + String getHomepageUrl(); /** * Gets the url of the source code repository. diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 13911101..38fd9665 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -101,14 +101,18 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { ac.setNoticeFileUrl(componentInfo.getNoticeFilePath()); } + if (componentInfo.getNoticeFileContent() != null) { + ac.setNoticeFileContent(componentInfo.getNoticeFileContent()); + } + for (LicenseInfo li : componentInfo.getLicenses()) { - addRawLicense(ac, li.getSpdxid(), li.getLicenseFilePath(), ORIGIN_COMPONENTINFO); + addRawLicense(ac, li.getSpdxid(), li.getLicenseFilePath(), li.getGivenLicenseText(), ORIGIN_COMPONENTINFO); } String copyrights = String.join("\n", componentInfo.getCopyrights()); ac.setCopyrights(copyrights); // check whether VendorUrl is included in input file or not - if (componentInfo.getUrl() != null) { - ac.setOssHomepage(componentInfo.getUrl()); + if (componentInfo.getHomepageUrl() != null) { + ac.setOssHomepage(componentInfo.getHomepageUrl()); } // check whether Source Reop Url is included in input file or not if (componentInfo.getSourceRepoUrl() != null) { @@ -130,14 +134,17 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { * @param appComponent a {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent} object. * @param name a {@link java.lang.String} object. * @param url a {@link java.lang.String} object. + * @param givenLicenseText a {@link java.lang.String} object. * @param origin a {@link java.lang.String} object. */ - public void addRawLicense(ApplicationComponent appComponent, String name, String url, String origin) { + public void addRawLicense(ApplicationComponent appComponent, String name, String url, String givenLicenseText, + String origin) { RawLicense mlic = this.modelFactory.newRawLicense(); mlic.setApplicationComponent(appComponent); mlic.setDeclaredLicense(name); mlic.setLicenseUrl(url); + mlic.setDeclaredLicenseContent(givenLicenseText); mlic.setOrigin(origin); String trace; trace = "+ Component/License info read from ComponentInfo data source"; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java index 5f76694b..e46a965a 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java @@ -16,4 +16,8 @@ public interface LicenseInfo { */ String getLicenseFilePath(); + /** + * @return the given license text (might be null) + */ + String getGivenLicenseText(); } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index 3640461e..5da29da7 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -40,6 +40,11 @@ public class ScancodeLicenseInfo implements LicenseInfo { */ private String licenseFilePath; + /** + * Text of license. + */ + private String givenLicenseText; + /** * The score of the license file. */ @@ -53,10 +58,11 @@ public class ScancodeLicenseInfo implements LicenseInfo { * @param defaultUrl the default URL of the license text * @param licenseScore the score for the license * @param licenseFilePath the path to the license file + * @param givenLicenseText the given license text (might be null) * @param licenseFileScore the score of the license file */ public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath, - double licenseFileScore) { + String givenLicenseText, double licenseFileScore) { super(); this.id = id; @@ -65,6 +71,7 @@ public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double l if (licenseFileScore >= ScancodeComponentInfo.this.minLicensefilePercentage) { this.licenseFilePath = licenseFilePath; this.licenseFileScore = licenseFileScore; + this.givenLicenseText = givenLicenseText; } else { this.licenseFilePath = defaultUrl; this.licenseFileScore = 0.0; @@ -105,6 +112,15 @@ public String getLicenseFilePath() { return this.licenseFilePath; } + /** + * @return givenLicenseText + */ + @Override + public String getGivenLicenseText() { + + return this.givenLicenseText; + } + /** * @return licenseFileScore */ @@ -145,6 +161,14 @@ public void setLicenseFilePath(String licenseFilePath) { this.licenseFilePath = licenseFilePath; } + /** + * @param givenLicenseText new value of {@link #getGivenLicenseText}. + */ + public void setGivenLicenseText(String givenLicenseText) { + + this.givenLicenseText = givenLicenseText; + } + /** * @param licenseFileScore new value of {@link #getLicenseFileScore}. */ @@ -152,6 +176,7 @@ public void setLicenseFileScore(double licenseFileScore) { this.licenseFileScore = licenseFileScore; } + } private static final double MIN_NOTICEFILE_PERCENTAGE = 0.0; @@ -164,6 +189,8 @@ public void setLicenseFileScore(double licenseFileScore) { private String noticeFilePath = null; + private String noticeFileContent; + private String url; private String sourceRepoUrl; @@ -223,28 +250,31 @@ public void clearCopyrights() { * @param licenseDefaultUrl the url of the generic license text * @param score the score of the license finding * @param filePath path to the license file + * @param givenLicenseText the license text * @param fileScore score of the license file */ public void addLicense(String licenseId, String licenseName, String licenseDefaultUrl, double score, String filePath, - double fileScore) { + String givenLicenseText, double fileScore) { if (this.licenses.containsKey(licenseId)) { ScancodeLicenseInfo existingLicenseInfo = this.licenses.get(licenseId); double resultingScore = Math.max(existingLicenseInfo.getLicenseScore(), score); String resultingFilePath = existingLicenseInfo.getLicenseFilePath(); + String resultingGivenText = existingLicenseInfo.getGivenLicenseText(); double resultingFileScore = existingLicenseInfo.getLicenseFileScore(); if (fileScore > existingLicenseInfo.getLicenseFileScore()) { resultingFilePath = filePath; resultingFileScore = fileScore; + resultingGivenText = givenLicenseText; } this.licenses.put(licenseId, new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, resultingScore, - resultingFilePath, resultingFileScore)); + resultingFilePath, resultingGivenText, resultingFileScore)); } else { if (score >= this.minLicenseScore) { - this.licenses.put(licenseId, - new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, filePath, fileScore)); + this.licenses.put(licenseId, new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, filePath, + givenLicenseText, fileScore)); } } } @@ -283,6 +313,14 @@ public void addNoticeFilePath(String path, double score) { } } + /** + * @param noticeFileContent new value of {@link #getNoticeFileContent}. + */ + public void setNoticeFileContent(String noticeFileContent) { + + this.noticeFileContent = noticeFileContent; + } + /** * Gets the path to the notice file (if any). * @@ -294,13 +332,19 @@ public String getNoticeFilePath() { return this.noticeFilePath; } + @Override + public String getNoticeFileContent() { + + return this.noticeFileContent; + } + /** * Gets the url of the projects homepage. * * @return url to the projects homepage */ @Override - public String getUrl() { + public String getHomepageUrl() { return this.url; } @@ -308,9 +352,9 @@ public String getUrl() { /** * Sets the url of the projects homepage. * - * @param url new value of {@link #getUrl()}. + * @param url new value of {@link #getHomepageUrl()}. */ - public void setUrl(String url) { + public void setHomepageUrl(String url) { this.url = url; } @@ -335,4 +379,5 @@ public void setSourceRepoUrl(String sourceRepoUrl) { this.sourceRepoUrl = sourceRepoUrl; } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java index f9ef78e8..b423e14b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java @@ -13,6 +13,7 @@ import org.springframework.stereotype.Component; import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter; @@ -53,6 +54,9 @@ public class ScancodeFileAdapter implements ComponentInfoAdapter { @Autowired private AllKindsPackageURLHandler packageURLHandler; + @Autowired + private DirectUrlWebContentProvider contentProvider; + /** * The constructor. */ @@ -207,11 +211,20 @@ private ScancodeComponentInfo determineScancodeInformation(String packageUrl) th double score = li.get("score").asDouble(); String licenseFilePath = file.get("path").asText(); licenseFilePath = normalizeLicenseUrl(packageUrl, licenseFilePath); + String givenLicenseText = null; + if (licenseFilePath != null && licenseFilePath.startsWith("file:")) { + givenLicenseText = this.contentProvider.getContentForUri(licenseFilePath).getContent(); + } componentScancodeInfos.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseFilePath, - file.get("percentage_of_license_text").asDouble()); + givenLicenseText, file.get("percentage_of_license_text").asDouble()); } } + if (componentScancodeInfos.getNoticeFilePath() != null + && componentScancodeInfos.getNoticeFilePath().startsWith("file:")) { + componentScancodeInfos.setNoticeFileContent( + this.contentProvider.getContentForUri(componentScancodeInfos.getNoticeFilePath()).getContent()); + } LOG.debug("Scancode info for package {}: {} license, {} copyrights, {} NOTICE files", packageUrl, componentScancodeInfos.getLicenses().size(), componentScancodeInfos.getCopyrights().size(), componentScancodeInfos.getNoticeFilePath() != null ? 1 : 0); @@ -272,7 +285,7 @@ private void applyCurations(String packageUrl, ScancodeComponentInfo componentSc for (JsonNode licenseNode : curations.get("licenses")) { String license = licenseNode.get("license").asText(); String url = licenseNode.get("url").asText(); - oneComponent.addLicense(license, license, "", 110, url, 110); + oneComponent.addLicense(license, license, "", 110, url, null, 110); } } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java index b997d4d6..eed9125d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java @@ -8,6 +8,11 @@ */ public interface TextPool { + /** + * The key used for null values. + */ + static String NULL_KEY = "NULL_KEY"; + /** * Store a text in the pool. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java index 4fca32e6..9209bd0b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java @@ -12,9 +12,6 @@ */ public class TextPoolImpl implements TextPool { - /** The key used for null values. */ - private static final String NULL_KEY = "NULL_KEY"; - private Map dataMap; /** diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java index afa9ae0f..262ea18a 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java @@ -39,6 +39,8 @@ public class ApplicationComponentImpl extends AbstractModelObject implements App private String noticeFileUrl; + private String noticeFileContentKey; + private String groupId; private String artifactId; @@ -156,8 +158,17 @@ public String getNoticeFileUrl() { @JsonIgnore public String getNoticeFileContent() { - return LicenseTextHelper - .replaceLongHtmlContent(this.licenseContentProvider.getContentForUri(this.noticeFileUrl).getContent()); + return LicenseTextHelper.replaceLongHtmlContent(retrieveTextFromPool(this.noticeFileContentKey)); + } + + /** + * Gets the text pool key of {@link #getNoticeFileContent()} + * + * @return the text pool key + */ + public String getNoticeFileContentKey() { + + return this.noticeFileContentKey; } /** {@inheritDoc} */ @@ -259,6 +270,24 @@ public void setNoticeFileUrl(String noticeFileUrl) { this.noticeFileUrl = noticeFileUrl; } + @Override + public void setNoticeFileContent(String noticeFileContent) { + + this.noticeFileContentKey = storeTextInPool(noticeFileContent); + + } + + /** + * Sets the text pool key of {@link #getNoticeFileContent()} + * + * @param noticeFileContentKey the key to set + */ + public void setNoticeFileContentKey(String noticeFileContentKey) { + + this.noticeFileContentKey = noticeFileContentKey; + + } + /** {@inheritDoc} */ @Override public void setOssModified(boolean ossModified) { @@ -313,6 +342,10 @@ public void completeData() { normalizedLicense.completeData(); } + if (this.noticeFileContentKey == null) { + setNoticeFileContent(this.licenseContentProvider.getContentForUri(this.noticeFileUrl).getContent()); + } + } @Override @@ -325,7 +358,6 @@ public String getCopyrights() { public void setCopyrights(String copyrights) { this.copyrights = copyrights; - // TODO Auto-generated method stub } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java index 21113fa8..5ca47d89 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java @@ -99,6 +99,7 @@ public NormalizedLicenseImpl(RawLicense rawLicense) { setApplicationComponent(rawLicense.getApplicationComponent()); this.declaredLicense = rawLicense.getDeclaredLicense(); this.licenseUrl = rawLicense.getLicenseUrl(); + setDeclaredLicenseContent(rawLicense.getDeclaredLicenseContent()); this.trace = rawLicense.getTrace(); } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java index 77eb5ab1..f11a899d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java @@ -5,6 +5,7 @@ package com.devonfw.tools.solicitor.model.impl.inventory; import com.devonfw.tools.solicitor.model.impl.AbstractModelObject; +import com.devonfw.tools.solicitor.model.impl.TextPool; import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; import com.devonfw.tools.solicitor.model.inventory.RawLicense; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -18,6 +19,8 @@ public class RawLicenseImpl extends AbstractModelObject implements RawLicense { private String licenseUrl; + private String declaredLicenseContentKey = TextPool.NULL_KEY; + private String trace; private String origin; @@ -55,6 +58,24 @@ public String getDeclaredLicense() { return this.declaredLicense; } + /** {@inheritDoc} */ + @Override + @JsonIgnore + public String getDeclaredLicenseContent() { + + return retrieveTextFromPool(this.declaredLicenseContentKey); + } + + /** + * Gets the text pool key of the {@link #getDeclaredLicenseContent()}. + * + * @return the key + */ + public String getDeclaredLicenseContentKey() { + + return this.declaredLicenseContentKey; + } + /** {@inheritDoc} */ @Override public String[] getHeadElements() { @@ -115,6 +136,23 @@ public void setLicenseUrl(String licenseUrl) { this.licenseUrl = licenseUrl; } + /** {@inheritDoc} */ + @Override + public void setDeclaredLicenseContent(String declaredLicenseContent) { + + this.declaredLicenseContentKey = storeTextInPool(declaredLicenseContent); + } + + /** + * Sets the text pool key of the {@link #getDeclaredLicenseContent()}. + * + * @param declaredLicenseContentKey the key + */ + public void setDeclaredLicenseContentKey(String declaredLicenseContentKey) { + + this.declaredLicenseContentKey = declaredLicenseContentKey; + } + /** {@inheritDoc} */ @Override public void setSpecialHandling(boolean specialHandling) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java index 18e99ff6..aa6a2efa 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java @@ -161,18 +161,25 @@ public interface ApplicationComponent { void setOssHomepage(String ossHomepage); /** - * This method sets the field ossHomepage. + * This method sets the field sourceRepoUrl. * - * @param ossHomepage the new value of the field ossHomepage + * @param sourceRepoUrl the new value of the field sourceRepoUrl */ - void setSourceRepoUrl(String ossHomepage); + void setSourceRepoUrl(String sourceRepoUrl); /** - * This method sets the field sourceRepoUrl. + * This method sets the field noticeFileUrl. * - * @param sourceRepoUrl the new value of the field sourceRepoUrl + * @param noticeFileUrl the new value of the field noticeFileUrl + */ + void setNoticeFileUrl(String noticeFileUrl); + + /** + * This method sets the field noticeFileContent. + * + * @param noticeFileContent the new value of the field noticeFileContent */ - void setNoticeFileUrl(String sourceRepoUrl); + void setNoticeFileContent(String noticeFileContent); /** * This method sets the field ossModified. diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java index 41c5322d..9eaebad1 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/RawLicense.java @@ -26,6 +26,13 @@ public interface RawLicense { */ String getLicenseUrl(); + /** + * This method gets the field declaredLicenseContent. + * + * @return the field declaredLicenseContent + */ + String getDeclaredLicenseContent(); + /** * This method gets the field trace. * @@ -68,6 +75,13 @@ public interface RawLicense { */ void setLicenseUrl(String licenseUrl); + /** + * This method sets the field declaredLicenseContent. + * + * @param declaredLicenseContent the new value of the field declaredLicenseContent + */ + void setDeclaredLicenseContent(String declaredLicenseContent); + /** * This method sets the field specialHandling. * diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 29934727..13b5f7ff 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -161,6 +161,7 @@ The internal business data model consists of 6 entities: | Property | Type | Description | declaredLicense | String | name of the declared license | licenseUrl | String | URL of the declared license +| declaredLicenseContent | String | license text as provided in the input data | trace | String | detail info of history of this data record | origin | String | origin of the raw license data; either the lowercase classname of the Reader or "scancode" if licensedata was taken from scancode results | specialHandling | boolean | _(for controlling rule processing)_ @@ -1557,6 +1558,7 @@ Changes in 1.8.0:: * https://github.com/devonfw/solicitor/issues/156: Included engagement name as member in class SolicitorSetup. * https://github.com/devonfw/solicitor/issues/158: Include library com.auth0/java-jwt to make it available for extensions. * https://github.com/devonfw/solicitor/issues/160: License texts are now included in the data model json. +* https://github.com/devonfw/solicitor/issues/162: Include added notice file content and license texts to the ComponentInfo interface. Changes in 1.7.0:: * https://github.com/devonfw/solicitor/issues/152: Enhancements for using java code in extensions. From 0698ff8090f8545dd21f1b50fcbb92ed4384a36d Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 3 Feb 2023 02:39:10 +0100 Subject: [PATCH 035/139] Version 1.8.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 1508e48a..85f0add3 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.8.0-SNAPSHOT + 1.8.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 9a6e0eb6..db03c83f 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.8.0-SNAPSHOT + 1.8.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 40b34924..3d0244df 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.8.0-SNAPSHOT + 1.8.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 47f5e9ab..4aa367c8 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.8.0-SNAPSHOT + 1.8.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 2170207f..89275c87 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.8.0-SNAPSHOT + 1.8.0 pom Solicitor Aggregator From 97a1362b65f30941311958b0ad5b197e9e6fbf10 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 3 Feb 2023 02:56:58 +0100 Subject: [PATCH 036/139] Set to SNAPSHOT of next minor version --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 85f0add3..d6023428 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.8.0 + 1.9.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index db03c83f..a0aa68be 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.8.0 + 1.9.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 13b5f7ff..0dc5b713 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1553,6 +1553,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.9.0:: + Changes in 1.8.0:: * https://github.com/devonfw/solicitor/issues/154: Corrected dependency declaration for solicitor-core.jar. * https://github.com/devonfw/solicitor/issues/156: Included engagement name as member in class SolicitorSetup. diff --git a/documentation/pom.xml b/documentation/pom.xml index 3d0244df..c8db3659 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.8.0 + 1.9.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 4aa367c8..196df49e 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.8.0 + 1.9.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 89275c87..93ac90fa 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.8.0 + 1.9.0-SNAPSHOT pom Solicitor Aggregator From 34a6f3ab3999d8ebc1241ec9d5331a0d255a878a Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:32:19 +0100 Subject: [PATCH 037/139] Changed handling of null values in TextPool (#165) --- app/pom.xml | 2 +- core/pom.xml | 2 +- .../tools/solicitor/model/impl/TextPool.java | 10 +++------ .../solicitor/model/impl/TextPoolImpl.java | 6 ++--- .../impl/inventory/NormalizedLicenseImpl.java | 22 +++++++++---------- .../model/impl/inventory/RawLicenseImpl.java | 3 +-- .../model/impl/TextPoolImplTest.java | 11 ++-------- documentation/master-solicitor.asciidoc | 3 +++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 27 insertions(+), 38 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 85f0add3..f7046625 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.8.0 + 1.8.1 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index db03c83f..fde8912b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.8.0 + 1.8.1 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java index eed9125d..2b000955 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPool.java @@ -8,23 +8,19 @@ */ public interface TextPool { - /** - * The key used for null values. - */ - static String NULL_KEY = "NULL_KEY"; - /** * Store a text in the pool. * * @param text the text to store in the pool; might be null - * @return the key of the text in the pool + * @return the key of the text in the pool; might be null (which is returned for a stored + * null value) */ String store(String text); /** * Retrieves a text from the pool * - * @param key the key of the text + * @param key the key of the text (might null which represents a null value) * @return the stored text; might be null if a null value was stored in the pool * @throws NoSuchElementException if nothing is stored under the given key */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java index 9209bd0b..11c91428 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/TextPoolImpl.java @@ -44,7 +44,7 @@ public String store(String text) { // special handling of null (null values never get stored in the map) if (text == null) { - return NULL_KEY; + return null; } String key = DigestUtils.sha256Hex(text); @@ -59,10 +59,8 @@ public String store(String text) { @Override public String retrieve(String key) { + // a null key represents a null string if (key == null) { - throw new NullPointerException("Key for pool must not be null"); - } - if (NULL_KEY.equals(key)) { return null; } String result = this.dataMap.get(key); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java index 5ca47d89..76d0a8ee 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/NormalizedLicenseImpl.java @@ -711,29 +711,29 @@ private void possiblyGuessLicenseUrl() { } /** - * If the {@link #guessedLicenseContentKey} is not yet set (i.e. the content is not yet set) it will be attempted to + * If the {@link #getGuessedLicenseContent()} is not yet set (i.e. the content is not yet set) it will be attempted to * fetch the content via the {@link #licenseContentProvider} and store it. */ private void possiblyFillGuessedLicenseContent() { - if (this.guessedLicenseContentKey == null) { + if (getGuessedLicenseContent() == null) { setGuessedLicenseContent(this.licenseContentProvider.getContentForUri(this.guessedLicenseUrl).getContent()); } } /** - * If the {@link #declaredLicenseContentKey} is not yet set(i.e. the content is not set yet) it will be attempted to + * If the {@link #getDeclaredLicenseContent} is not yet set(i.e. the content is not set yet) it will be attempted to * fetch the content via the {@link #licenseContentProvider} and store it. */ private void possiblyFillDeclaredLicenseContent() { - if (this.declaredLicenseContentKey == null) { + if (getDeclaredLicenseContent() == null) { setDeclaredLicenseContent(this.licenseContentProvider.getContentForUri(this.licenseUrl).getContent()); } } /** - * If the {@link #normalizedLicenseContentKey} is not yet set (i.e. the content is not set yet) it will be attempted + * If the {@link #getNormalizedLicenseContent()} is not yet set (i.e. the content is not set yet) it will be attempted * to fill the content. This is tried via two ways: *
      *
    • if {@link #normalizedLicenseUrl} equals {@link #licenseUrl} the {@link #getDeclaredLicenseContent()} will be @@ -743,7 +743,7 @@ private void possiblyFillDeclaredLicenseContent() { */ private void possiblyFillNormalizedLicenseContent() { - if (this.normalizedLicenseContentKey == null) { + if (getNormalizedLicenseContent() == null) { if (this.normalizedLicenseUrl != null && this.normalizedLicenseUrl.equals(this.licenseUrl)) { setNormalizedLicenseContent(getDeclaredLicenseContent()); } else { @@ -754,7 +754,7 @@ private void possiblyFillNormalizedLicenseContent() { } /** - * If the {@link #effectiveNormalizedLicenseContentKey} is not yet set (i.e. the content is not set yet) it will be + * If the {@link #getEffectiveNormalizedLicenseContent()} is not yet set (i.e. the content is not set yet) it will be * attempted to fill the content. This is tried via two ways: *
        *
      • if {@link #effectiveNormalizedLicenseUrl} equals {@link #normalizedLicenseUrl} the @@ -764,7 +764,7 @@ private void possiblyFillNormalizedLicenseContent() { */ private void possiblyFillEffectiveNormalizedLicenseContent() { - if (this.effectiveNormalizedLicenseContentKey == null) { + if (getEffectiveNormalizedLicenseContent() == null) { if (this.effectiveNormalizedLicenseUrl != null && this.effectiveNormalizedLicenseUrl.equals(this.normalizedLicenseUrl)) { setEffectiveNormalizedLicenseContent(getNormalizedLicenseContent()); @@ -776,12 +776,12 @@ private void possiblyFillEffectiveNormalizedLicenseContent() { } /** - * If the {@link #licenseRefContentKey} is not yet set(i.e. the content is not set yet) it will be attempted to fetch - * the content via the {@link #licenseContentProvider} and store it. + * If the {@link #getLicenseRefContent()} is not yet set(i.e. the content is not set yet) it will be attempted to + * fetch the content via the {@link #licenseContentProvider} and store it. */ private void possiblyFillLicenseRefContent() { - if (this.licenseRefContentKey == null) { + if (getLicenseRefContent() == null) { setLicenseRefContent(this.licenseContentProvider.getContentForUri(this.licenseRefUrl).getContent()); } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java index f11a899d..e0881717 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/RawLicenseImpl.java @@ -5,7 +5,6 @@ package com.devonfw.tools.solicitor.model.impl.inventory; import com.devonfw.tools.solicitor.model.impl.AbstractModelObject; -import com.devonfw.tools.solicitor.model.impl.TextPool; import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; import com.devonfw.tools.solicitor.model.inventory.RawLicense; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -19,7 +18,7 @@ public class RawLicenseImpl extends AbstractModelObject implements RawLicense { private String licenseUrl; - private String declaredLicenseContentKey = TextPool.NULL_KEY; + private String declaredLicenseContentKey; private String trace; diff --git a/core/src/test/java/com/devonfw/tools/solicitor/model/impl/TextPoolImplTest.java b/core/src/test/java/com/devonfw/tools/solicitor/model/impl/TextPoolImplTest.java index fb341221..b90bf469 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/model/impl/TextPoolImplTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/model/impl/TextPoolImplTest.java @@ -17,9 +17,9 @@ class TextPoolImplTest { void testStoreAndRetrieveNull() { TextPool pool = new TextPoolImpl(); - assertEquals("NULL_KEY", pool.store(null)); + assertNull(pool.store(null)); - assertNull(pool.retrieve("NULL_KEY")); + assertNull(pool.retrieve(null)); } @Test @@ -29,13 +29,6 @@ void testThrowExceptionForUnknownKey() { assertThrows(NoSuchElementException.class, () -> pool.retrieve("foo")); } - @Test - void testThrowExceptionForNullKey() { - - TextPool pool = new TextPoolImpl(); - assertThrows(NullPointerException.class, () -> pool.retrieve(null)); - } - @Test void testStoreAndRetrieveData() { diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 13b5f7ff..30d157de 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1553,6 +1553,9 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.8.1:: +* https://github.com/devonfw/solicitor/issues/164: Fixed a bug which might result in license texts not being retrieved. + Changes in 1.8.0:: * https://github.com/devonfw/solicitor/issues/154: Corrected dependency declaration for solicitor-core.jar. * https://github.com/devonfw/solicitor/issues/156: Included engagement name as member in class SolicitorSetup. diff --git a/documentation/pom.xml b/documentation/pom.xml index 3d0244df..3a02b671 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.8.0 + 1.8.1 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 4aa367c8..68203052 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.8.0 + 1.8.1 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 89275c87..b955c85d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.8.0 + 1.8.1 pom Solicitor Aggregator From 1c11362dca7a2d76c0e990df3d4663e2dbb41587 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 26 Apr 2023 17:13:58 +0200 Subject: [PATCH 038/139] Fixed #167. (#170) --- .../componentinfo/scancode/ScancodeFileAdapter.java | 7 ++++++- documentation/master-solicitor.asciidoc | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java index b423e14b..c211ff3c 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java @@ -285,7 +285,12 @@ private void applyCurations(String packageUrl, ScancodeComponentInfo componentSc for (JsonNode licenseNode : curations.get("licenses")) { String license = licenseNode.get("license").asText(); String url = licenseNode.get("url").asText(); - oneComponent.addLicense(license, license, "", 110, url, null, 110); + String givenLicenseText = null; + if (url != null && url.startsWith("file:")) { + givenLicenseText = this.contentProvider.getContentForUri(url).getContent(); + } + + oneComponent.addLicense(license, license, "", 110, url, givenLicenseText, 110); } } } diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 8970e590..6f15df7d 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1554,6 +1554,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.9.0:: +* https://github.com/devonfw/solicitor/issues/167: Fixed a bug which prevented license URLs given in scancode curations (see <>) to be resolved properly when they point to the local file system (starting with `file:`). Changes in 1.8.1:: * https://github.com/devonfw/solicitor/issues/164: Fixed a bug which might result in license texts not being retrieved. From 1d089a77aae0e76c9d3325ceb22a98dd08963b6b Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 27 Apr 2023 16:51:01 +0200 Subject: [PATCH 039/139] Improvements of mapping ScanCode data to Solicitor data model (#171) (#173) --- .../tools/solicitor/common/LogMessages.java | 5 +- .../web/DirectUrlWebContentProvider.java | 40 ++++++++- .../ComponentInfoInventoryProcessor.java | 12 ++- .../scancode/ScancodeComponentInfo.java | 30 +++---- .../scancode/ScancodeFileAdapter.java | 83 ++++++++++++++++--- .../src/main/resources/application.properties | 6 +- documentation/files/application.properties | 11 ++- documentation/master-solicitor.asciidoc | 2 + 8 files changed, 152 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 8204c9f9..010d663b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -83,7 +83,10 @@ public enum LogMessages { "The experimental feature for enriching the inventory with scancode data is DEACTIVATED"), // COMPONENT_INFO_READ(56, "External component information was read for {} out of {} ApplicationComponents"), // CURATIONS_NOT_EXISTING(57, "Curations file '{}' not found. No curations will be applied."), // - CURATIONS_PROCESSING(58, "Curations file '{}' exists. Applying curations."); + CURATIONS_PROCESSING(58, "Curations file '{}' exists. Applying curations."), // + COMPONENTINFO_NO_LICENSES(59, "ComponentInfo for '{}' does not contain any license. Keeping licenses from Reader."), // + CLASSPATHEXCEPTION_WITHOUT_GPL(60, "ClassPathException was found but no GPL License exists for {}"), // + CLASSPATHEXCEPTION_MULTIPLE_GPL(61, "ClassPathException was found but there are multiple GPL Licenses for {}"); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProvider.java index 0eef87db..c8c075c0 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProvider.java @@ -8,6 +8,8 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,6 +53,13 @@ public WebContent getContentForUri(String url) { LOG.info(LogMessages.SKIP_DOWNLOAD.msg(), url); return new WebContent(null); } + String urlWithoutLines; + int startOfLineInfo = url.indexOf("#L"); + String lineInfo = null; + if (startOfLineInfo >= 0) { + lineInfo = url.substring(startOfLineInfo, url.length()); + url = url.substring(0, startOfLineInfo); + } try { webContentUrl = new URL(url); } catch (MalformedURLException e) { @@ -64,7 +73,8 @@ public WebContent getContentForUri(String url) { try (InputStream is = webContentUrl.openConnection().getInputStream(); Scanner s = new Scanner(is)) { s.useDelimiter("\\A"); String result = s.hasNext() ? s.next() : ""; - return new WebContent(result); + + return new WebContent(possiblyExtractLines(result, lineInfo)); } catch (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug("Could not retieve content for url '" + url + "'", e); @@ -74,4 +84,32 @@ public WebContent getContentForUri(String url) { return new WebContent(null); } + /** + * @param input + * @param lineInfo + * @return + */ + private String possiblyExtractLines(String input, String lineInfo) { + + if (lineInfo == null) { + return input; + } + Pattern pattern = Pattern.compile("#L(\\d+)(-L(\\d+))?"); + Matcher matcher = pattern.matcher(lineInfo); + if (matcher.find()) { + int startLine = Integer.parseInt(matcher.group(1)); + int endLine = Integer.parseInt(matcher.group(3) != null ? matcher.group(3) : matcher.group(1)); + String[] splitted = input.split("\\n"); + StringBuffer result = new StringBuffer(); + for (int i = 0; i < splitted.length; i++) { + if (i + 1 >= startLine && i + 1 <= endLine) { + result.append(splitted[i]).append("\n"); + } + } + return result.toString(); + } else { + throw new IllegalStateException("Regex did not find line info - this seems to be a bug."); + } + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 38fd9665..c119209b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -95,7 +95,6 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { } if (componentInfo != null) { statistics.componentsWithComponentInfo = 1; - ac.removeAllRawLicenses(); if (componentInfo.getNoticeFilePath() != null) { ac.setNoticeFileUrl(componentInfo.getNoticeFilePath()); @@ -105,9 +104,16 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { ac.setNoticeFileContent(componentInfo.getNoticeFileContent()); } - for (LicenseInfo li : componentInfo.getLicenses()) { - addRawLicense(ac, li.getSpdxid(), li.getLicenseFilePath(), li.getGivenLicenseText(), ORIGIN_COMPONENTINFO); + if (componentInfo.getLicenses().size() > 0) { + ac.removeAllRawLicenses(); + for (LicenseInfo li : componentInfo.getLicenses()) { + addRawLicense(ac, li.getSpdxid(), li.getLicenseFilePath(), li.getGivenLicenseText(), ORIGIN_COMPONENTINFO); + } + } else { + LOG.info(LogMessages.COMPONENTINFO_NO_LICENSES.msg(), + (ac.getGroupId() != null ? ac.getGroupId() + "/" : "") + ac.getArtifactId() + "/" + ac.getVersion()); } + String copyrights = String.join("\n", componentInfo.getCopyrights()); ac.setCopyrights(copyrights); // check whether VendorUrl is included in input file or not diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index 5da29da7..9406324c 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -46,9 +46,9 @@ public class ScancodeLicenseInfo implements LicenseInfo { private String givenLicenseText; /** - * The score of the license file. + * The score of the license file. This is measured as number of lines which contain license info */ - private double licenseFileScore; + private int licenseFileScore; /** * The constructor. @@ -59,22 +59,22 @@ public class ScancodeLicenseInfo implements LicenseInfo { * @param licenseScore the score for the license * @param licenseFilePath the path to the license file * @param givenLicenseText the given license text (might be null) - * @param licenseFileScore the score of the license file + * @param licenseFileScore the score of the license file - number of lines with license info */ public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath, - String givenLicenseText, double licenseFileScore) { + String givenLicenseText, int licenseFileScore) { super(); this.id = id; this.spdxid = spdxid; this.licenseScore = licenseScore; - if (licenseFileScore >= ScancodeComponentInfo.this.minLicensefilePercentage) { + if (licenseFileScore >= ScancodeComponentInfo.this.minLicensefileNumberOfLines) { this.licenseFilePath = licenseFilePath; this.licenseFileScore = licenseFileScore; this.givenLicenseText = givenLicenseText; } else { this.licenseFilePath = defaultUrl; - this.licenseFileScore = 0.0; + this.licenseFileScore = 0; } } @@ -124,7 +124,7 @@ public String getGivenLicenseText() { /** * @return licenseFileScore */ - public double getLicenseFileScore() { + public int getLicenseFileScore() { return this.licenseFileScore; } @@ -172,7 +172,7 @@ public void setGivenLicenseText(String givenLicenseText) { /** * @param licenseFileScore new value of {@link #getLicenseFileScore}. */ - public void setLicenseFileScore(double licenseFileScore) { + public void setLicenseFileScore(int licenseFileScore) { this.licenseFileScore = licenseFileScore; } @@ -195,7 +195,7 @@ public void setLicenseFileScore(double licenseFileScore) { private String sourceRepoUrl; - private double minLicensefilePercentage; + private int minLicensefileNumberOfLines; private double minLicenseScore; @@ -203,13 +203,13 @@ public void setLicenseFileScore(double licenseFileScore) { * The constructor. * * @param minLicenseScore the minimum score to take license findings into account - * @param minLicensefilePercentage the minimum percentage of license text to possibly use file as license file + * @param minLicensefileNumberOfLines the minimum number of licens text lines to possibly use file as license file */ - public ScancodeComponentInfo(double minLicenseScore, double minLicensefilePercentage) { + public ScancodeComponentInfo(double minLicenseScore, int minLicensefileNumberOfLines) { super(); this.minLicenseScore = minLicenseScore; - this.minLicensefilePercentage = minLicensefilePercentage; + this.minLicensefileNumberOfLines = minLicensefileNumberOfLines; } /** @@ -251,10 +251,10 @@ public void clearCopyrights() { * @param score the score of the license finding * @param filePath path to the license file * @param givenLicenseText the license text - * @param fileScore score of the license file + * @param fileScore score of the license file - measured as number of lines which were detected as license text */ public void addLicense(String licenseId, String licenseName, String licenseDefaultUrl, double score, String filePath, - String givenLicenseText, double fileScore) { + String givenLicenseText, int fileScore) { if (this.licenses.containsKey(licenseId)) { ScancodeLicenseInfo existingLicenseInfo = this.licenses.get(licenseId); @@ -262,7 +262,7 @@ public void addLicense(String licenseId, String licenseName, String licenseDefau double resultingScore = Math.max(existingLicenseInfo.getLicenseScore(), score); String resultingFilePath = existingLicenseInfo.getLicenseFilePath(); String resultingGivenText = existingLicenseInfo.getGivenLicenseText(); - double resultingFileScore = existingLicenseInfo.getLicenseFileScore(); + int resultingFileScore = existingLicenseInfo.getLicenseFileScore(); if (fileScore > existingLicenseInfo.getLicenseFileScore()) { resultingFilePath = filePath; resultingFileScore = fileScore; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java index c211ff3c..0623d9c1 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java @@ -4,6 +4,8 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,7 +45,7 @@ public class ScancodeFileAdapter implements ComponentInfoAdapter { private double minLicenseScore; - private double minLicensefilePercentage; + private int minLicensefileNumberOfLines; private boolean curationsExistenceLogged; @@ -51,6 +53,8 @@ public class ScancodeFileAdapter implements ComponentInfoAdapter { private boolean featureLogged = false; + private double licenseToTextRatioToTakeCompleteFile = 90; + @Autowired private AllKindsPackageURLHandler packageURLHandler; @@ -110,14 +114,14 @@ public void setMinLicenseScore(double minLicenseScore) { } /** - * Sets minLicensefilePercentage. + * Sets minLicensefileNumberOfLines. * - * @param minLicensefilePercentage new value of minLicensefilePercentage. + * @param minLicensefileNumberOfLines new value of minLicensefileNumberOfLines. */ - @Value("${solicitor.scancode.min-licensefile-percentage}") - public void setMinLicensefilePercentage(double minLicensefilePercentage) { + @Value("${solicitor.scancode.min-licensefile-number-of-lines}") + public void setMinLicensefileNumberOfLines(int minLicensefileNumberOfLines) { - this.minLicensefilePercentage = minLicensefilePercentage; + this.minLicensefileNumberOfLines = minLicensefileNumberOfLines; } /** @@ -173,7 +177,7 @@ private boolean isFeatureActive() { private ScancodeComponentInfo determineScancodeInformation(String packageUrl) throws ComponentInfoAdapterException { ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore, - this.minLicensefilePercentage); + this.minLicensefileNumberOfLines); String packagePathPart = this.packageURLHandler.pathFor(packageUrl); String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json"; @@ -195,6 +199,8 @@ private ScancodeComponentInfo determineScancodeInformation(String packageUrl) th componentScancodeInfos.addNoticeFilePath("file:" + this.repoBasePath + "/" + this.packageURLHandler.pathFor(packageUrl) + "/" + file.get("path").asText(), 100.0); } + double licenseTextRatio = file.get("percentage_of_license_text").asDouble(); + boolean takeCompleteFile = licenseTextRatio >= this.licenseToTextRatioToTakeCompleteFile; for (JsonNode cr : file.get("copyrights")) { if (cr.has("copyright")) { componentScancodeInfos.addCopyright(cr.get("copyright").asText()); @@ -203,13 +209,64 @@ private ScancodeComponentInfo determineScancodeInformation(String packageUrl) th } } + // special handling for Classpath-exception-2.0 + Map spdxIdMap = new HashMap<>(); + boolean classPathExceptionExists = false; + int numberOfGplLicenses = 0; + for (JsonNode li : file.get("licenses")) { + String licenseName = li.get("spdx_license_key").asText(); + if ("Classpath-exception-2.0".equals(licenseName)) { + classPathExceptionExists = true; + } + if (!spdxIdMap.containsKey(licenseName)) { + spdxIdMap.put(licenseName, licenseName); + if (licenseName.startsWith("GPL")) { + numberOfGplLicenses++; + } + } + } + if (classPathExceptionExists) { + if (numberOfGplLicenses == 0) { + LOG.warn(LogMessages.CLASSPATHEXCEPTION_WITHOUT_GPL.msg(), packageUrl); + } else if (numberOfGplLicenses > 1) { + LOG.warn(LogMessages.CLASSPATHEXCEPTION_MULTIPLE_GPL.msg(), packageUrl); + } else { + LOG.debug("Adjusting GPL license to contain WITH Classpath-execption-2.0 for " + packageUrl); + for (String licenseName : spdxIdMap.keySet()) { + if (licenseName.startsWith("GPL")) { + spdxIdMap.put(licenseName, licenseName + " WITH Classpath-exception-2.0"); + } + } + // do not output the Classpath-exception-2.0 as separate License + spdxIdMap.remove("Classpath-exception-2.0"); + } + } for (JsonNode li : file.get("licenses")) { String licenseid = li.get("key").asText(); String licenseName = li.get("spdx_license_key").asText(); + String effectiveLicenseName = spdxIdMap.get(licenseName); + if (effectiveLicenseName == null) { + // not contained in map --> this must be the Classpath-exception-2.0 + continue; + } else { + licenseName = effectiveLicenseName; + if (licenseName.endsWith("WITH Classpath-exception-2.0")) { + licenseid = licenseid + "WITH Classpath-exception-2.0"; + } + } String licenseDefaultUrl = li.get("scancode_text_url").asText(); licenseDefaultUrl = normalizeLicenseUrl(packageUrl, licenseDefaultUrl); double score = li.get("score").asDouble(); String licenseFilePath = file.get("path").asText(); + int startLine = li.get("start_line").asInt(); + int endLine = li.get("end_line").asInt(); + if (!takeCompleteFile) { + licenseFilePath += "#L" + startLine; + if (endLine != startLine) { + licenseFilePath += "-L" + endLine; + } + } + licenseFilePath = normalizeLicenseUrl(packageUrl, licenseFilePath); String givenLicenseText = null; if (licenseFilePath != null && licenseFilePath.startsWith("file:")) { @@ -217,9 +274,12 @@ private ScancodeComponentInfo determineScancodeInformation(String packageUrl) th } componentScancodeInfos.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseFilePath, - givenLicenseText, file.get("percentage_of_license_text").asDouble()); + givenLicenseText, endLine - startLine); } } + // if (componentScancodeInfos.getLicenses().size() == 0) { + // componentScancodeInfos.addLicense("unknown", "unknown", "", 100.0, "", "", 0); + // } if (componentScancodeInfos.getNoticeFilePath() != null && componentScancodeInfos.getNoticeFilePath().startsWith("file:")) { componentScancodeInfos.setNoticeFileContent( @@ -290,7 +350,7 @@ private void applyCurations(String packageUrl, ScancodeComponentInfo componentSc givenLicenseText = this.contentProvider.getContentForUri(url).getContent(); } - oneComponent.addLicense(license, license, "", 110, url, givenLicenseText, 110); + oneComponent.addLicense(license, license, "", 110, url, givenLicenseText, Integer.MAX_VALUE); } } } @@ -317,7 +377,10 @@ private String normalizeLicenseUrl(String packageUrl, String licenseFilePath) { if (licenseFilePath != null) { if (licenseFilePath.startsWith("http")) { // TODO - adjustedLicenseFilePath = licenseFilePath.replace("github.com", "raw.github.com").replace("/tree", ""); + adjustedLicenseFilePath = licenseFilePath.replace( + "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses", + "https://scancode-licensedb.aboutcode.org"); + adjustedLicenseFilePath = adjustedLicenseFilePath.replace("github.com", "raw.github.com").replace("/tree", ""); } else { adjustedLicenseFilePath = "file:" + this.repoBasePath + "/" + this.packageURLHandler.pathFor(packageUrl) + "/" + licenseFilePath; diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index b30cba44..f146c658 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -47,9 +47,9 @@ solicitor.feature-flag.scancode=false ## Parameters for controlling the processing of scancode information # minimum score of detected license findings to be taken into account for Solicitor processing -solicitor.scancode.min-license-score=80.0 -# minimum "percentage_of_license_text" for a file to be possibly taken as license text -solicitor.scancode.min-licensefile-percentage=50.0 +solicitor.scancode.min-license-score=90.0 +# minimum number of lines of detected license text for a file to be possibly taken as license text +solicitor.scancode.min-licensefile-number-of-lines=5 # filename of the curationy.yaml file solicitor.scancode.curations-filename=output/curations.yaml # base path of the file repo where sources and scancode information is stored diff --git a/documentation/files/application.properties b/documentation/files/application.properties index 874da74d..f146c658 100644 --- a/documentation/files/application.properties +++ b/documentation/files/application.properties @@ -10,7 +10,7 @@ logging.level.org.drools=WARN logging.level.com.zaxxer.hikari.util.DriverDataSource=ERROR # logging.level.com.devonfw.tools.solicitor.ruleengine.drools.AuditEntryBuilder=DEBUG -logging.file=solicitor.log +logging.file.name=solicitor.log logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx} @@ -47,9 +47,9 @@ solicitor.feature-flag.scancode=false ## Parameters for controlling the processing of scancode information # minimum score of detected license findings to be taken into account for Solicitor processing -solicitor.scancode.min-license-score=80.0 -# minimum "percentage_of_license_text" for a file to be possibly taken as license text -solicitor.scancode.min-licensefile-percentage=50.0 +solicitor.scancode.min-license-score=90.0 +# minimum number of lines of detected license text for a file to be possibly taken as license text +solicitor.scancode.min-licensefile-number-of-lines=5 # filename of the curationy.yaml file solicitor.scancode.curations-filename=output/curations.yaml # base path of the file repo where sources and scancode information is stored @@ -67,6 +67,9 @@ solicitor.tolerate-missing-input=false # and might override properties given here spring.profiles.include=extension +# do not allow spring to start any webserver - even if the components are included in the classpath +spring.main.web-application-type=none + ## Default values of the properties giving information about a possibly active extension # this will be overriden by definitions given in application-extension.properties in the # the extension classpath diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 6f15df7d..ad789785 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1554,8 +1554,10 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.9.0:: +* https://github.com/devonfw/solicitor/issues/171: Multiple improvements in processing of ScanCode results. * https://github.com/devonfw/solicitor/issues/167: Fixed a bug which prevented license URLs given in scancode curations (see <>) to be resolved properly when they point to the local file system (starting with `file:`). + Changes in 1.8.1:: * https://github.com/devonfw/solicitor/issues/164: Fixed a bug which might result in license texts not being retrieved. From be843f72303ebc9e6cb006656a8006c14c3af91e Mon Sep 17 00:00:00 2001 From: duph97 Date: Thu, 27 Apr 2023 16:54:20 +0200 Subject: [PATCH 040/139] Fix Spellcheck Errors (#172) --- documentation/master-solicitor.asciidoc | 2 +- solicitor.dict | 28 +++++++++++++------------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index ad789785..fb295a5a 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1583,7 +1583,7 @@ Changes in 1.4.0:: * https://github.com/devonfw/solicitor/issues/141: Improved robustness of report generation in cases where PackageURL can not be determined (e.g. if data originates from CSV reader). * https://github.com/devonfw/solicitor/issues/139: Provide extension interface to allow reading information about components/licenses from alternative sources. See <>. -* https://github.com/devonfw/solicitor/issues/137: Internal restructuration of Solicitor modules which allows Solicitor code to be used as dependency in other projects. +* https://github.com/devonfw/solicitor/issues/137: Internal restructuring of Solicitor modules which allows Solicitor code to be used as dependency in other projects. * https://github.com/devonfw/solicitor/issues/129: Added spellcheck support within documentation for run-together words like camel cased ones. * https://github.com/devonfw/solicitor/issues/130: Fixed a bug where the guessedLicenseUrl and guessedLicenseUrlAuditInfo fields were not filled correctly in the aggregated inventory. * Added reader for data generated by OSS Review Toolkit (ORT). See <>. diff --git a/solicitor.dict b/solicitor.dict index 5df7788b..a59ea484 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -1,25 +1,19 @@ -scancode -SOLI -vm +auth Capsulate -Dloader -Dpropertyname -Dsome -RHS -Unlicense -WTFPL -Zlib -Zope cfgdir conf deliverables -drt +Dloader +Dpropertyname +Dsome dt +drt ec eug fas gradle http +jwt licenseRefUrl licenseToIgnoreN licenseurls @@ -28,9 +22,17 @@ natively nd ort oss +RHS +scancode +SOLI sql +Unlicense url urls velo venv -zA \ No newline at end of file +vm +WTFPL +zA +Zlib +Zope \ No newline at end of file From 32aa3ad072b3a0838e19c2cbc473694608dd336e Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 27 Apr 2023 17:31:11 +0200 Subject: [PATCH 041/139] Version 1.9.0 (#174) --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index d6023428..9b5af954 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.9.0-SNAPSHOT + 1.9.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index a0aa68be..dd11580d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.9.0-SNAPSHOT + 1.9.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index c8db3659..0dd5262b 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.9.0-SNAPSHOT + 1.9.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 196df49e..444b0a54 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.9.0-SNAPSHOT + 1.9.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 93ac90fa..94c23d68 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.9.0-SNAPSHOT + 1.9.0 pom Solicitor Aggregator From ce814f9f85ef9ced25385ebf8b5466ab8fd28c3b Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 27 Apr 2023 21:45:17 +0200 Subject: [PATCH 042/139] Set to SNAPSHOT of next minor version --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 9b5af954..5266b6fd 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.9.0 + 1.10.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index dd11580d..b18760d6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.9.0 + 1.10.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index fb295a5a..e3d0d686 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1553,6 +1553,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.10.0:: + Changes in 1.9.0:: * https://github.com/devonfw/solicitor/issues/171: Multiple improvements in processing of ScanCode results. * https://github.com/devonfw/solicitor/issues/167: Fixed a bug which prevented license URLs given in scancode curations (see <>) to be resolved properly when they point to the local file system (starting with `file:`). diff --git a/documentation/pom.xml b/documentation/pom.xml index 0dd5262b..a14af2ef 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.9.0 + 1.10.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 444b0a54..ee6b4f14 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.9.0 + 1.10.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 94c23d68..a47926ad 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.9.0 + 1.10.0-SNAPSHOT pom Solicitor Aggregator From 0ec46c3cca876bda59d84390059875e439d3f123 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 12 May 2023 09:26:04 +0200 Subject: [PATCH 043/139] Add download URLs for binaries and sources to ApplicationComponent (# 175 with PR #176) --- .../tools/solicitor/InventoryProcessor.java | 6 +++ .../common/packageurl/PackageURLHandler.java | 8 +++ .../AbstractSingleKindPackageURLHandler.java | 15 ++++++ .../impl/DelegatingPackageURLHandlerImpl.java | 7 +++ .../impl/MavenPackageURLHandlerImpl.java | 13 +++++ .../impl/NpmPackageURLHandlerImpl.java | 14 +++++ .../impl/PyPIPackageURLHandlerImpl.java | 14 +++++ .../componentinfo/ComponentInfo.java | 15 +++++- .../ComponentInfoInventoryProcessor.java | 10 ++++ .../scancode/ScancodeComponentInfo.java | 46 +++++++++++++++++ .../scancode/ScancodeFileAdapter.java | 45 ++++++++++++++++ .../DefaultDownloadUrlInventoryProcessor.java | 51 +++++++++++++++++++ .../inventory/ApplicationComponentImpl.java | 37 +++++++++++++- .../model/inventory/ApplicationComponent.java | 28 ++++++++++ .../solicitor/templates/ScancodeScript.vm | 9 ++++ documentation/master-solicitor.asciidoc | 29 +++++++++-- 16 files changed, 341 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/model/DefaultDownloadUrlInventoryProcessor.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java index b2e3af3c..1ac2ebaa 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/InventoryProcessor.java @@ -22,6 +22,12 @@ */ public interface InventoryProcessor { + /** + * Processing order: After the readers have read the raw data. Use in {@link Order} annotation of the implementing + * spring bean. + */ + public static final int AFTER_READERS = 50; + /** * Processing order: Execute before rule engine. Use in {@link Order} annotation of the implementing spring bean. */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/PackageURLHandler.java b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/PackageURLHandler.java index dd558408..fc20d243 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/PackageURLHandler.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/PackageURLHandler.java @@ -13,6 +13,14 @@ public interface PackageURLHandler { */ String sourceDownloadUrlFor(String packageUrl); + /** + * Get the URL for downloading the package referenced by the package URL. + * + * @param packageUrl the package URL of the package (stringified form) + * @return the URL to download the package + */ + String packageDownloadUrlFor(String packageUrl); + /** * Return the (relative) path to be used when accessing the references package in some tree structure * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/AbstractSingleKindPackageURLHandler.java b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/AbstractSingleKindPackageURLHandler.java index 1674dd77..878196c6 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/AbstractSingleKindPackageURLHandler.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/AbstractSingleKindPackageURLHandler.java @@ -52,6 +52,21 @@ public String sourceDownloadUrlFor(String packageUrl) { */ protected abstract String doSourceDownloadUrlFor(PackageURL purl); + @Override + public String packageDownloadUrlFor(String packageUrl) { + + PackageURL purl = parsePackageURLAndCheckType(packageUrl); + return doPackageDownloadUrlFor(purl); + } + + /** + * Returns the package download URL. To be implemented in subclasses. + * + * @param purl the package url + * @return the package download URL + */ + protected abstract String doPackageDownloadUrlFor(PackageURL purl); + /** * Parse the given String into a {@link PackageURL} and assures if it can be handled. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/DelegatingPackageURLHandlerImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/DelegatingPackageURLHandlerImpl.java index b3f6973b..e822818c 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/DelegatingPackageURLHandlerImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/DelegatingPackageURLHandlerImpl.java @@ -39,6 +39,13 @@ public String sourceDownloadUrlFor(String packageUrl) { return findApplicableSingleKindHandler(packageURL).sourceDownloadUrlFor(packageUrl); } + @Override + public String packageDownloadUrlFor(String packageUrl) { + + PackageURL packageURL = parse(packageUrl); + return findApplicableSingleKindHandler(packageURL).packageDownloadUrlFor(packageUrl); + } + @Override public String pathFor(String packageUrl) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/MavenPackageURLHandlerImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/MavenPackageURLHandlerImpl.java index bae112ea..3bdf6dbf 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/MavenPackageURLHandlerImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/MavenPackageURLHandlerImpl.java @@ -45,6 +45,19 @@ public String doSourceDownloadUrlFor(PackageURL purl) { return sb.toString(); } + @Override + public String doPackageDownloadUrlFor(PackageURL purl) { + + StringBuffer sb = new StringBuffer(this.repoBaseUrl); + sb.append(purl.getNamespace().replace('.', '/')).append("/"); + sb.append(purl.getName()).append("/"); + sb.append(purl.getVersion()).append("/"); + sb.append(purl.getName()).append("-").append(purl.getVersion()); + sb.append(".jar"); + + return sb.toString(); + } + @Override public String doSourceArchiveSuffixFor(PackageURL purl) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/NpmPackageURLHandlerImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/NpmPackageURLHandlerImpl.java index e2a9982f..aeb9bcea 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/NpmPackageURLHandlerImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/NpmPackageURLHandlerImpl.java @@ -46,6 +46,20 @@ protected String doSourceDownloadUrlFor(PackageURL purl) { return sb.toString(); } + @Override + protected String doPackageDownloadUrlFor(PackageURL purl) { + + StringBuffer sb = new StringBuffer(this.repoBaseUrl); + if (purl.getNamespace() != null) { + sb.append(purl.getNamespace()).append("/"); + } + sb.append(purl.getName()).append("/-/"); + sb.append(purl.getName()).append("-"); + sb.append(purl.getVersion()).append(".tgz"); + + return sb.toString(); + } + @Override protected String doSourceArchiveSuffixFor(PackageURL packageURL) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/PyPIPackageURLHandlerImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/PyPIPackageURLHandlerImpl.java index 2a6c4964..ee217e2c 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/PyPIPackageURLHandlerImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/PyPIPackageURLHandlerImpl.java @@ -45,6 +45,20 @@ public String doSourceDownloadUrlFor(PackageURL purl) { return sb.toString(); } + @Override + public String doPackageDownloadUrlFor(PackageURL purl) { + + // Take the sdist - same as above + StringBuffer sb = new StringBuffer(this.repoBaseUrl); + sb.append("source/"); + sb.append(purl.getName().substring(0, 1)).append("/"); + sb.append(purl.getName()).append("/"); + sb.append(purl.getName()).append("-"); + sb.append(purl.getVersion()).append(".tar.gz"); + + return sb.toString(); + } + @Override public String doSourceArchiveSuffixFor(PackageURL purl) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java index 84d9f77b..3f8f531a 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java @@ -47,8 +47,21 @@ public interface ComponentInfo { /** * Gets the url of the source code repository. * - * @return sourceRepoUrl to the license text + * @return url to source code repository */ String getSourceRepoUrl(); + /** + * Gets the url for downloading the package/component. + * + * @return url to download the package + */ + String getPackageDownloadUrl(); + + /** + * Gets the url for downloading the sources of the package/component. + * + * @return url to download the sources of the package/component + */ + String getSourceDownloadUrl(); } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index c119209b..31b373a8 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -112,6 +112,11 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { } else { LOG.info(LogMessages.COMPONENTINFO_NO_LICENSES.msg(), (ac.getGroupId() != null ? ac.getGroupId() + "/" : "") + ac.getArtifactId() + "/" + ac.getVersion()); + for (RawLicense rl : ac.getRawLicenses()) { + String trace = rl.getTrace() + System.lineSeparator() + + "+ ComponentInfo available but without license information - keeping data from Reader"; + rl.setTrace(trace); + } } String copyrights = String.join("\n", componentInfo.getCopyrights()); @@ -124,6 +129,11 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { if (componentInfo.getSourceRepoUrl() != null) { ac.setSourceRepoUrl(componentInfo.getSourceRepoUrl()); } + + // always overwrite the download URLs - even if componentInfo does not contain any data + ac.setPackageDownloadUrl(componentInfo.getPackageDownloadUrl()); + ac.setSourceDownloadUrl(componentInfo.getSourceDownloadUrl()); + } else { // no ComponentInfos info found for ac } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index 9406324c..0597aae9 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -199,6 +199,10 @@ public void setLicenseFileScore(int licenseFileScore) { private double minLicenseScore; + private String packageDownloadUrl; + + private String sourceDownloadUrl; + /** * The constructor. * @@ -380,4 +384,46 @@ public void setSourceRepoUrl(String sourceRepoUrl) { this.sourceRepoUrl = sourceRepoUrl; } + /** + * Gets the url to download the package. + * + * @return url to download the package + */ + @Override + public String getPackageDownloadUrl() { + + return this.packageDownloadUrl; + } + + /** + * Sets the url to download the package. + * + * @param packageDownloadUrl new value of {@link #getPackageDownloadUrl()}. + */ + public void setPackageDownloadUrl(String packageDownloadUrl) { + + this.packageDownloadUrl = packageDownloadUrl; + } + + /** + * Gets the url to download the sources of the package. + * + * @return url to download the sources of the package + */ + @Override + public String getSourceDownloadUrl() { + + return this.sourceDownloadUrl; + } + + /** + * Sets the url to download the sources of the package. + * + * @param sourceDownloadUrl new value of {@link #getSourceDownloadUrl()}. + */ + public void setSourceDownloadUrl(String sourceDownloadUrl) { + + this.sourceDownloadUrl = sourceDownloadUrl; + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java index 0623d9c1..4828ff67 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java @@ -143,6 +143,7 @@ public ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAda if (componentScancodeInfos == null) { return null; } + addOriginData(packageUrl, componentScancodeInfos); applyCurations(packageUrl, componentScancodeInfos); return componentScancodeInfos; @@ -295,6 +296,50 @@ private ScancodeComponentInfo determineScancodeInformation(String packageUrl) th return componentScancodeInfos; } + /** + * Adds the data about the origin of the package which is (optionally) contained in file "origin.yaml" + * + * @param packageUrl the identifier of the package + * @param componentScancodeInfos the componentScancodeInfos to add the origin data to + * @throws ComponentInfoAdapterException if there was an error when reading the file + */ + private void addOriginData(String packageUrl, ScancodeComponentInfo componentScancodeInfos) + throws ComponentInfoAdapterException { + + String packagePathPart = this.packageURLHandler.pathFor(packageUrl); + String path = this.repoBasePath + "/" + packagePathPart + "/origin.yaml"; + + File originFile = new File(path); + if (!originFile.exists()) { + LOG.debug("No origin info available for PackageURL '{}'", packageUrl); + return; + } + LOG.debug("Found origin info for PackageURL '{}'", packageUrl); + + try (InputStream is = new FileInputStream(originFile)) { + + JsonNode originYaml = yamlMapper.readTree(is); + + String sourceDownloadUrl = originYaml.get("sourceDownloadUrl") != null + ? originYaml.get("sourceDownloadUrl").asText() + : null; + String packageDownloadUrl = originYaml.get("packageDownloadUrl") != null + ? originYaml.get("packageDownloadUrl").asText() + : null; + String note = originYaml.get("note") != null ? originYaml.get("note").asText() : null; + if (note != null) { + LOG.debug("Note: " + note); + } + + componentScancodeInfos.setSourceDownloadUrl(sourceDownloadUrl); + componentScancodeInfos.setPackageDownloadUrl(packageDownloadUrl); + + } catch (IOException e) { + throw new ComponentInfoAdapterException("Could not read origin.yaml", e); + } + + } + /** * Checks for the existence of curations for the given package in the local file system and applies them to the * component scancode infos. diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/DefaultDownloadUrlInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/model/DefaultDownloadUrlInventoryProcessor.java new file mode 100644 index 00000000..ebfb31bf --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/DefaultDownloadUrlInventoryProcessor.java @@ -0,0 +1,51 @@ +package com.devonfw.tools.solicitor.model; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.InventoryProcessor; +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.masterdata.Application; + +/** + * An {@link InventoryProcessor} which fills the {@link ApplicationComponent#getPackageDownloadUrl()} and + * {@link ApplicationComponent#getSourceDownloadUrl()} with default values derived from the PackageURL if there is no + * other data yet. + * + */ +@Component +@Order(InventoryProcessor.AFTER_READERS) +public class DefaultDownloadUrlInventoryProcessor implements InventoryProcessor { + + @Autowired + private AllKindsPackageURLHandler packageURLHandler; + + /** + * The constructor. + */ + public DefaultDownloadUrlInventoryProcessor() { + + } + + @Override + public void processInventory(ModelRoot modelRoot) { + + for (Application application : modelRoot.getEngagement().getApplications()) { + for (ApplicationComponent ac : application.getApplicationComponents()) { + String packageUrl = ac.getPackageUrl(); + if (packageUrl != null && !packageUrl.isEmpty()) { + if (ac.getPackageDownloadUrl() == null) { + ac.setPackageDownloadUrl(this.packageURLHandler.packageDownloadUrlFor(packageUrl)); + } + if (ac.getSourceDownloadUrl() == null) { + ac.setSourceDownloadUrl(this.packageURLHandler.sourceDownloadUrlFor(packageUrl)); + } + + } + } + } + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java index 262ea18a..2544692b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java @@ -53,6 +53,10 @@ public class ApplicationComponentImpl extends AbstractModelObject implements App private String copyrights; + private String packageDownloadUrl; + + private String sourceDownloadUrl; + private List normalizedLicenses = new ArrayList<>(); private List rawLicenses = new ArrayList<>(); @@ -107,7 +111,7 @@ public String[] getDataElements() { return new String[] { this.groupId, this.artifactId, this.version, getRepoType(), getPackageUrl(), getOssHomepage(), getSourceRepoUrl(), getNoticeFileUrl(), getNoticeFileContent(), getUsagePattern().toString(), - isOssModified() ? "true" : "false", getCopyrights() }; + isOssModified() ? "true" : "false", getCopyrights(), getPackageDownloadUrl(), getSourceDownloadUrl() }; } /** {@inheritDoc} */ @@ -122,7 +126,8 @@ public String getGroupId() { public String[] getHeadElements() { return new String[] { "groupId", "artifactId", "version", "repoType", "packageUrl", "ossHomepage", "sourceRepoUrl", - "noticeFileUrl", "noticeFileContent", "usagePattern", "ossModified", "copyrights" }; + "noticeFileUrl", "noticeFileContent", "usagePattern", "ossModified", "copyrights", "packageDownloadUrl", + "sourceDownloadUrl" }; } /** {@inheritDoc} */ @@ -213,6 +218,20 @@ public boolean isOssModified() { return this.ossModified; } + /** {@inheritDoc} */ + @Override + public String getPackageDownloadUrl() { + + return this.packageDownloadUrl; + } + + /** {@inheritDoc} */ + @Override + public String getSourceDownloadUrl() { + + return this.sourceDownloadUrl; + } + /** * This method gets the field licenseContentProvider. * @@ -361,6 +380,20 @@ public void setCopyrights(String copyrights) { } + /** {@inheritDoc} */ + @Override + public void setPackageDownloadUrl(String packageDownloadUrl) { + + this.packageDownloadUrl = packageDownloadUrl; + } + + /** {@inheritDoc} */ + @Override + public void setSourceDownloadUrl(String sourceDownloadUrl) { + + this.sourceDownloadUrl = sourceDownloadUrl; + } + /** * This method sets the field licenseContentProvider. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java index aa6a2efa..b1488317 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java @@ -132,6 +132,20 @@ public interface ApplicationComponent { String getCopyrights(); + /** + * Gets the url to download the package. + * + * @return url to download the package + */ + public String getPackageDownloadUrl(); + + /** + * Gets the url to download the sources of the package. + * + * @return url to download the sources of the package + */ + public String getSourceDownloadUrl(); + /** * Sets the {@link Application}. * @@ -226,6 +240,20 @@ public interface ApplicationComponent { */ public void setPackageUrl(String packageUrl); + /** + * Sets the url to download the package. + * + * @param packageDownloadUrl new value of {@link #getPackageDownloadUrl()}. + */ + public void setPackageDownloadUrl(String packageDownloadUrl); + + /** + * Sets the url to download the sources of the package. + * + * @param sourceDownloadUrl new value of {@link #getSourceDownloadUrl()}. + */ + public void setSourceDownloadUrl(String sourceDownloadUrl); + /** * Complete the data of this object by setting members which are derived from other members. */ diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm index d5aa4b8a..30e72258 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/ScancodeScript.vm @@ -28,8 +28,17 @@ echo "These artifacts need to be downloaded manually:" > need-to-retrieve-manual then cd $purlhandler.pathFor($artifact.packageUrl) rm sources.completed + rm sources.failed rm -r sources rm sources.jar + if [ ! -f origin.yaml ] + then + echo "# This file contains metadata about the orgin of the package and the sources." > origin.yaml + echo "# This file was automatically created but might manually be edited if the contained data is not correct" >> origin.yaml + echo "sourceDownloadUrl: $purlhandler.sourceDownloadUrlFor($artifact.packageUrl)" >> origin.yaml + echo "packageDownloadUrl: $purlhandler.packageDownloadUrlFor($artifact.packageUrl)" >> origin.yaml + echo "# note: to add comments: write them here and remove the hash at the beginning of the line" >> origin.yaml + fi curl -# $purlhandler.sourceDownloadUrlFor($artifact.packageUrl) -o sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) #if ( $purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) == "jar" ) unzip -q -d sources sources.$purlhandler.sourceArchiveSuffixFor($artifact.packageUrl) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index e3d0d686..eb7d4b22 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -153,6 +153,8 @@ The internal business data model consists of 6 entities: | noticeFileUrl | String | URL referencing a NOTICE file to be included in the attributions (optional, see <>) | noticeFileContent | String | resolved content of noticeFileUrl (optional, see <>) | copyrights | String | Copyright statements found in the components metadata / code (optional, see <>) +| packageDownloadUrl | String | URL for downloading the component (optional, see <>) +| sourceDownloadUrl | String | URL for downloading the sources of the component (optional, see <>) |=== ==== RawLicense @@ -1348,15 +1350,35 @@ This will download all sources and process them via ScanCode. This might take several hours to complete. Results are stored in subdirectory `Source` of the directory `output` and is organized in a tree structure given by the https://github.com/package-url/purl-spec[PackageURL] of the ApplicationComponents. +===== Origin file +The Scancode integration scripts try to download ApplicationComponent sources from default URLs derived from the PackageUrl (e.g. Maven Central). In cases where the sources are not available at these locations, the download will fail (and the subsequent source scan will be skipped). In this case it is possible to manually download the sources from some other location and store it in the directory structure. Restarting the Scancode integration script might then perform the source scan. + +To be able to document the (non default) origin of the ApplicationComponent sources a file `origin.yaml` is created in the components directory in the file system. If the failed source download has been performed manually it is possible to edit this file and correct the data given in this file. + +[source,yaml] +---- +# This file contains metadata about the orgin of the package and the sources. +# This file was automatically created but might manually be edited if the contained data is not correct +sourceDownloadUrl: https://url/pointing/to/the/source/archive.jar <1> +packageDownloadUrl: https://url/pointing/to/the/binary/archive.jar <2> +# note: to add comments: write them here and remove the hash at the beginning of the line (not yet processed by Solicitor) +---- +<1> URL for downloading the sources - will be available as property `ApplicationComponent.sourceDownloadUrl` in the Solicitor data model. +<2> URL for downloading the binaries - will be available as property `ApplicationComponent.packageDownloadUrl` in the Solicitor data model. + +The content of the file `origin.yaml` currently just affects the above given two properties, it does not affect the downloading of sources by the scripts. + + ==== Solicitor 2nd run Execute _Solicitor_ a second time. After reading the component/license information from the Readers (but before starting the rule engine) -_Solicitor_ will try to look up ScanCode information from the directory tree in `output/Sources` for all processed ApplicationComponents. If information is found for an ApplicationsComponent the following is done: +_Solicitor_ will try to look up ScanCode information from the directory tree in `output/Sources` for all processed ApplicationComponents. If information is found for an ApplicationComponent the following is done: * License information (including URL of license text) as obtained from the Readers is replaced by the license info found by ScanCode * Copyrights are taken from ScanCode results * Info on NOTICE file is taken from the ScanCode results -* If the ScanCode results contain information about a project URL then this is stored as `ossHomepage` +* If the ScanCode results contain information about project URLs this is stored as `sourceRepoUrl` and/or `ossHomepage` +* `sourceDownloadUrl` and `packageDownloadUrl` are set to the values given in file `origin.yaml` ==== Output Main target of the additional information obtained from ScanCode is currently the new report `Attributions_PROJECTNAME.html` which lists @@ -1392,7 +1414,7 @@ artifacts: . ---- <1> Path of the package information as used in the file tree. Derived from the PackageURL. -<2> URL of the project, will be stored as `ossHomepage`. (Optional: no change if not existing.) +<2> URL of the project, will be stored as `sourceRepoUrl`. (Optional: no change if not existing.) <3> Licenses to set. Optional. If defined then all found licenses will be replaced by the list of licenses given here. <4> SPDX identifier of license. <5> URL pointing to license text. @@ -1554,6 +1576,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.10.0:: +* https://github.com/devonfw/solicitor/issues/175: Introduce new properties `packageDownloadUrl` and `sourceDownloadUrl` in `ApplicationComponent`. Process file `origin.yaml` within <> to be able to set these properties to non default values. Changes in 1.9.0:: * https://github.com/devonfw/solicitor/issues/171: Multiple improvements in processing of ScanCode results. From e814054b011e9ec7ad4c0f2f6d9be882c41fad38 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 12 May 2023 10:02:14 +0200 Subject: [PATCH 044/139] Do not cache local files (#177 with PR #178) --- .../FilesystemCachingContentProvider.java | 30 ++++++++++--------- documentation/master-solicitor.asciidoc | 1 + 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/content/FilesystemCachingContentProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/common/content/FilesystemCachingContentProvider.java index 098218e9..f1b1ee9b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/content/FilesystemCachingContentProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/content/FilesystemCachingContentProvider.java @@ -65,22 +65,24 @@ public C loadFromNext(String url) { C result = super.loadFromNext(url); - File file = new File(this.resourceDirectory + "/" + getKey(url)); - File targetDir = file.getParentFile(); - try { - IOHelper.checkAndCreateLocation(file); - } catch (SolicitorRuntimeException e) { - LOG.error(LogMessages.COULD_NOT_CREATE_CACHE.msg(), targetDir.getAbsolutePath()); - return result; - } - try (FileWriter fw = new FileWriter(file)) { - if (result != null && result.asString() != null) { - fw.append(result.asString()); + if (!url.startsWith("file:")) { + // data of URLs which resolve to local file will not be cached + File file = new File(this.resourceDirectory + "/" + getKey(url)); + File targetDir = file.getParentFile(); + try { + IOHelper.checkAndCreateLocation(file); + } catch (SolicitorRuntimeException e) { + LOG.error(LogMessages.COULD_NOT_CREATE_CACHE.msg(), targetDir.getAbsolutePath()); + return result; + } + try (FileWriter fw = new FileWriter(file)) { + if (result != null && result.asString() != null) { + fw.append(result.asString()); + } + } catch (IOException e) { + LOG.error("Could not write data to file cache."); } - } catch (IOException e) { - LOG.error("Could not write data to file cache."); } - return result; } diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index eb7d4b22..ade0b30b 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1577,6 +1577,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.10.0:: * https://github.com/devonfw/solicitor/issues/175: Introduce new properties `packageDownloadUrl` and `sourceDownloadUrl` in `ApplicationComponent`. Process file `origin.yaml` within <> to be able to set these properties to non default values. +* https://github.com/devonfw/solicitor/issues/177: Contents of local resources (`file:`) are no longer cached in FilesystemCachingContentProvider. Changes in 1.9.0:: * https://github.com/devonfw/solicitor/issues/171: Multiple improvements in processing of ScanCode results. From 2df7f858606d429d31643a24cfce47bd18d74df3 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 12 May 2023 10:09:31 +0200 Subject: [PATCH 045/139] Version 1.10.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 5266b6fd..24e7c933 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.10.0-SNAPSHOT + 1.10.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index b18760d6..e5b4dd80 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.10.0-SNAPSHOT + 1.10.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index a14af2ef..983fb26c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.10.0-SNAPSHOT + 1.10.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index ee6b4f14..38e03417 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.10.0-SNAPSHOT + 1.10.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index a47926ad..35ccb096 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.10.0-SNAPSHOT + 1.10.0 pom Solicitor Aggregator From 1863a31aa20fb830e6cd1be77ec118037fc4f10a Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 12 May 2023 10:12:14 +0200 Subject: [PATCH 046/139] Set to SNAPSHOT of next minor version --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 24e7c933..52540b03 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.10.0 + 1.11.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index e5b4dd80..cb7d024a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.10.0 + 1.11.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 983fb26c..33d8f71b 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.10.0 + 1.11.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 38e03417..3c59025f 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.10.0 + 1.11.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 35ccb096..f83c76f4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.10.0 + 1.11.0-SNAPSHOT pom Solicitor Aggregator From ee0f6b18d9bd754335fca89caec73541d1ff7268 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 12 May 2023 10:14:40 +0200 Subject: [PATCH 047/139] Prepare userguide for next release --- documentation/master-solicitor.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index ade0b30b..03ca7a75 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1575,6 +1575,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.11.0:: + Changes in 1.10.0:: * https://github.com/devonfw/solicitor/issues/175: Introduce new properties `packageDownloadUrl` and `sourceDownloadUrl` in `ApplicationComponent`. Process file `origin.yaml` within <> to be able to set these properties to non default values. * https://github.com/devonfw/solicitor/issues/177: Contents of local resources (`file:`) are no longer cached in FilesystemCachingContentProvider. From 4fad72ccab3f71ef5b372d2880ff506b00c835e4 Mon Sep 17 00:00:00 2001 From: duph97 Date: Mon, 12 Jun 2023 13:44:58 +0200 Subject: [PATCH 048/139] added NameMapping rules (#179) * added NameMapping rules * Updated release notes --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../rules/LicenseNameMappingSample.xls | Bin 86016 -> 89088 bytes documentation/master-solicitor.asciidoc | 1 + 2 files changed, 1 insertion(+) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls index f6a38ea5fcb99075177f2b2055da96de567ccd4b..513946d0217eedb2f907f41d30bfbca10f7e72a9 100644 GIT binary patch delta 18985 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 R^Xl_QexGY?-5Rvg{~tQejIaOz delta 15231 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 A1ONa4 diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 03ca7a75..bec2c248 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1576,6 +1576,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.11.0:: +* https://github.com/devonfw/solicitor/pull/179: Added further name mapping rules. Changes in 1.10.0:: * https://github.com/devonfw/solicitor/issues/175: Introduce new properties `packageDownloadUrl` and `sourceDownloadUrl` in `ApplicationComponent`. Process file `origin.yaml` within <> to be able to set these properties to non default values. From 9020010c0ec0b5ef83be048364ea8c2c1d4fdc5a Mon Sep 17 00:00:00 2001 From: chrimih <101123170+chrimih@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:17:32 +0200 Subject: [PATCH 049/139] Do not raise NullPointer during GuessedLicenseUrlContent creation (#181) * Do not raise NullPointer during GuessedLicenseUrlContent creation --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../GuessedLicenseUrlContentFactory.java | 2 +- .../GuessedLicenseUrlContentFactoryTest.java | 14 ++++++++++++++ documentation/master-solicitor.asciidoc | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/licensetexts/GuessedLicenseUrlContentFactory.java b/core/src/main/java/com/devonfw/tools/solicitor/licensetexts/GuessedLicenseUrlContentFactory.java index b249d381..a2fedb47 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/licensetexts/GuessedLicenseUrlContentFactory.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/licensetexts/GuessedLicenseUrlContentFactory.java @@ -16,7 +16,7 @@ public class GuessedLicenseUrlContentFactory implements ContentFactory> to be able to set these properties to non default values. From ae9d18a295bedf0c4524764e8eaa2025e7b64550 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:43:20 +0200 Subject: [PATCH 050/139] Version 1.11.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 52540b03..8a1fe158 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.11.0-SNAPSHOT + 1.11.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index cb7d024a..97d290d5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.11.0-SNAPSHOT + 1.11.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 33d8f71b..c3b0d48c 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.11.0-SNAPSHOT + 1.11.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 3c59025f..1e5fe78c 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.11.0-SNAPSHOT + 1.11.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index f83c76f4..ea180ca3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.11.0-SNAPSHOT + 1.11.0 pom Solicitor Aggregator From 76c8a405b49612e88c758dadf5c74bd7e2b0b2c6 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:50:01 +0200 Subject: [PATCH 051/139] Set version to SNAPSHOT of next minor release --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 8a1fe158..a0b970e2 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.11.0 + 1.12.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 97d290d5..ec4a3555 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.11.0 + 1.12.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index c3b0d48c..2a88f761 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.11.0 + 1.12.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 1e5fe78c..f963744d 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.11.0 + 1.12.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index ea180ca3..dcb0f1f0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.11.0 + 1.12.0-SNAPSHOT pom Solicitor Aggregator From 2d054f6ef6728ed61046d41081d84b9993f835f3 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 12 Jun 2023 16:56:02 +0200 Subject: [PATCH 052/139] Prepare userguide for next release --- documentation/master-solicitor.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index cc1af213..69a3849d 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1575,6 +1575,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.12.0:: + Changes in 1.11.0:: * https://github.com/devonfw/solicitor/pull/179: Added further name mapping rules. * https://github.com/devonfw/solicitor/issues/168: Fixed NullPointerException for blank license in License URL Guessing. From afe1117106eaaf7de9447a74044f7a6ed50ab3ea Mon Sep 17 00:00:00 2001 From: malkam <108339027+mahmoudAlkam@users.noreply.github.com> Date: Wed, 2 Aug 2023 17:25:22 +0200 Subject: [PATCH 053/139] Extension of the data structure "ComponentInfo" and "ScancodeComponentInfo" in Solicitor (#183) --- .../componentinfo/ComponentInfo.java | 15 ++++ .../ComponentInfoInventoryProcessor.java | 38 ++++++++- .../scancode/ScancodeComponentInfo.java | 40 +++++++++ .../inventory/ApplicationComponentImpl.java | 43 +++++++++- .../model/inventory/ApplicationComponent.java | 28 +++++++ .../ComponentInfoInventoryProcessorTest.java | 83 +++++++++++++++++++ .../impl/ApplicationComponentImplTest.java | 56 +++++++++++++ documentation/master-solicitor.asciidoc | 3 + 8 files changed, 301 insertions(+), 5 deletions(-) create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessorTest.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/model/impl/ApplicationComponentImplTest.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java index 3f8f531a..4ffd7ede 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java @@ -1,6 +1,7 @@ package com.devonfw.tools.solicitor.componentinfo; import java.util.Collection; +import java.util.List; /** * Data structure which holds information about a component which comes from an external data source, like the results @@ -64,4 +65,18 @@ public interface ComponentInfo { * @return url to download the sources of the package/component */ String getSourceDownloadUrl(); + + /** + * This method gets the field dataStatus. + * + * @return the field dataStatus + */ + String getDataStatus(); + + /** + * Gets the traceability notes of the component. + * + * @return the traceability notes + */ + List getTraceabilityNotes(); } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 31b373a8..34288dd3 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -1,5 +1,7 @@ package com.devonfw.tools.solicitor.componentinfo; +import java.util.Collection; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -70,10 +72,14 @@ public void processInventory(ModelRoot modelRoot) { } /** - * Process a single {@link ApplicationComponent}. + * Processes a single {@link ApplicationComponent} by looking up license information from an external data source, + * such as a scancode file store. If license information is found, it updates the relevant properties of the + * {@link ApplicationComponent} with the data obtained from this source. The method also handles cases when no license + * information is found or when there is an error reading the component info data source. * - * @param ac application component - * @return processing statistics + * @param ac The {@link ApplicationComponent} to be processed. + * @return A {@link Statistics} object representing the processing statistics. + * @throws SolicitorRuntimeException If there is an exception when reading the component info data source. */ private Statistics processApplicationComponent(ApplicationComponent ac) { @@ -81,6 +87,7 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { statistics.componentsTotal = 1; if (ac.getPackageUrl() != null) { + // Try to get component information from the available ComponentInfoAdapters ComponentInfo componentInfo = null; try { for (ComponentInfoAdapter cia : this.componentInfoAdapters) { @@ -96,6 +103,13 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { if (componentInfo != null) { statistics.componentsWithComponentInfo = 1; + // Set dataStatus and traceabilityNotes of the ApplicationComponent + ac.setDataStatus(componentInfo.getDataStatus()); + // Format and set the traceabilityNotes in the ApplicationComponent + String formattedTraceabilityNotes = formatTraceabilityNotes(componentInfo); + ac.setTraceabilityNotes(formattedTraceabilityNotes); + + // Update the notice file URL and content if available if (componentInfo.getNoticeFilePath() != null) { ac.setNoticeFileUrl(componentInfo.getNoticeFilePath()); } @@ -104,6 +118,7 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { ac.setNoticeFileContent(componentInfo.getNoticeFileContent()); } + // Process licenses if available if (componentInfo.getLicenses().size() > 0) { ac.removeAllRawLicenses(); for (LicenseInfo li : componentInfo.getLicenses()) { @@ -143,6 +158,23 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { return statistics; } + /** + * Formats the traceability notes from the given {@link ComponentInfo} by concatenating to a single string using line + * separators. + * + * @param componentInfo The {@link ComponentInfo} containing the traceability notes. + * @return A formatted {@link String} representing the traceability notes, separated by the long separator. + */ + public String formatTraceabilityNotes(ComponentInfo componentInfo) { + + Collection traceabilityNotes = componentInfo.getTraceabilityNotes(); + if (traceabilityNotes != null && !traceabilityNotes.isEmpty()) { + return String.join(System.lineSeparator(), traceabilityNotes); + } else { + return ""; + } + } + /** * Adds a {@link com.devonfw.tools.solicitor.model.inventory.RawLicense} to the given * {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent}. diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index 0597aae9..b3824a45 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -2,6 +2,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; @@ -203,6 +204,10 @@ public void setLicenseFileScore(int licenseFileScore) { private String sourceDownloadUrl; + private String dataStatus; + + private List traceabilityNotes; + /** * The constructor. * @@ -426,4 +431,39 @@ public void setSourceDownloadUrl(String sourceDownloadUrl) { this.sourceDownloadUrl = sourceDownloadUrl; } + /** + * {@inheritDoc} + */ + @Override + public String getDataStatus() { + + return this.dataStatus; + } + + /** + * Gets the traceability notes of the component. + * + * @return the traceability notes + */ + @Override + public List getTraceabilityNotes() { + + return this.traceabilityNotes; + } + + /** + * @param dataStatus new value of {@link #getDataStatus}. + */ + public void setDataStatus(String dataStatus) { + + this.dataStatus = dataStatus; + } + + /** + * @param traceabilityNotes new value of {@link #getTraceabilityNotes}. + */ + public void setTraceabilityNotes(List traceabilityNotes) { + + this.traceabilityNotes = traceabilityNotes; + } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java index 2544692b..f5016e1a 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java @@ -63,6 +63,10 @@ public class ApplicationComponentImpl extends AbstractModelObject implements App private ContentProvider licenseContentProvider; + private String dataStatus; + + private String traceabilityNotes; + /** {@inheritDoc} */ @Override public void addNormalizedLicense(NormalizedLicense normalizedLicense) { @@ -111,7 +115,8 @@ public String[] getDataElements() { return new String[] { this.groupId, this.artifactId, this.version, getRepoType(), getPackageUrl(), getOssHomepage(), getSourceRepoUrl(), getNoticeFileUrl(), getNoticeFileContent(), getUsagePattern().toString(), - isOssModified() ? "true" : "false", getCopyrights(), getPackageDownloadUrl(), getSourceDownloadUrl() }; + isOssModified() ? "true" : "false", getCopyrights(), getPackageDownloadUrl(), getSourceDownloadUrl(), + getDataStatus(), getTraceabilityNotes() }; } /** {@inheritDoc} */ @@ -127,7 +132,7 @@ public String[] getHeadElements() { return new String[] { "groupId", "artifactId", "version", "repoType", "packageUrl", "ossHomepage", "sourceRepoUrl", "noticeFileUrl", "noticeFileContent", "usagePattern", "ossModified", "copyrights", "packageDownloadUrl", - "sourceDownloadUrl" }; + "sourceDownloadUrl", "dataStatus", "traceabilityNotes" }; } /** {@inheritDoc} */ @@ -394,6 +399,40 @@ public void setSourceDownloadUrl(String sourceDownloadUrl) { this.sourceDownloadUrl = sourceDownloadUrl; } + /** + * {@inheritDoc} + */ + @Override + public String getDataStatus() { + + return this.dataStatus; + } + + /** {@inheritDoc} */ + + @Override + public String getTraceabilityNotes() { + + return this.traceabilityNotes; + } + + /** + * {@inheritDoc} + */ + @Override + public void setDataStatus(String dataStatus) { + + this.dataStatus = dataStatus; + } + + /** {@inheritDoc} */ + + @Override + public void setTraceabilityNotes(String traceabilityNotes) { + + this.traceabilityNotes = traceabilityNotes; + } + /** * This method sets the field licenseContentProvider. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java index b1488317..5280e349 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java @@ -146,6 +146,20 @@ public interface ApplicationComponent { */ public String getSourceDownloadUrl(); + /** + * This method gets the field dataStatus. + * + * @return the field dataStatus + */ + String getDataStatus(); + + /** + * This method gets the field traceabilityNotes. + * + * @return the field traceabilityNotes + */ + String getTraceabilityNotes(); + /** * Sets the {@link Application}. * @@ -254,6 +268,20 @@ public interface ApplicationComponent { */ public void setSourceDownloadUrl(String sourceDownloadUrl); + /** + * This method sets the field dataStatus. + * + * @param dataStatus the new value of the field dataStatus + */ + void setDataStatus(String dataStatus); + + /** + * This method sets the field traceabilityNotes. + * + * @param traceabilityNotes the new value of the field traceabilityNotes + */ + void setTraceabilityNotes(String traceabilityNotes); + /** * Complete the data of this object by setting members which are derived from other members. */ diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessorTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessorTest.java new file mode 100644 index 00000000..d9372577 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessorTest.java @@ -0,0 +1,83 @@ +package com.devonfw.tools.solicitor.componentinfo; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +/** + * This class contains JUnit test methods for the {@link ComponentInfoInventoryProcessor} class. + */ +class ComponentInfoInventoryProcessorTest { + /** + * Test the {@link ComponentInfoInventoryProcessor#formatTraceabilityNotes(ComponentInfo)} method with a mock + * ComponentInfo containing traceability notes. + */ + @Test + public void testFormatTraceabilityNotes() { + + // Create a mock ComponentInfo with multiline traceability notes + ComponentInfo componentInfo = Mockito.mock(ComponentInfo.class); + List traceabilityNotes = Arrays.asList("Note 1", "Note 2", "Note 3"); + Mockito.when(componentInfo.getTraceabilityNotes()).thenReturn(traceabilityNotes); + + // Create an instance of ComponentInfoInventoryProcessor + ComponentInfoInventoryProcessor processor = new ComponentInfoInventoryProcessor(); + + // Call the formatTraceabilityNotes method with the mocked ComponentInfo + String formattedNotes = processor.formatTraceabilityNotes(componentInfo); + + // Define the expected formatted notes string with the long separator + String expectedFormattedNotes = "Note 1" + System.lineSeparator() + "Note 2" + System.lineSeparator() + "Note 3"; + + // Assert that the formatted notes match the expected formatted notes + Assertions.assertEquals(expectedFormattedNotes, formattedNotes); + } + + /** + * Test the {@link ComponentInfoInventoryProcessor#formatTraceabilityNotes(ComponentInfo)} method with a mock + * ComponentInfo containing an empty list of traceability notes. + */ + @Test + public void testFormatTraceabilityNotesWithEmptyList() { + + // Create a mock ComponentInfo with an empty list of traceability notes + ComponentInfo componentInfo = Mockito.mock(ComponentInfo.class); + List emptyList = Collections.emptyList(); + Mockito.when(componentInfo.getTraceabilityNotes()).thenReturn(emptyList); + + // Create an instance of ComponentInfoInventoryProcessor + ComponentInfoInventoryProcessor processor = new ComponentInfoInventoryProcessor(); + + // Call the formatTraceabilityNotes method with the mocked ComponentInfo + String formattedNotes = processor.formatTraceabilityNotes(componentInfo); + + // Assert that the formatted notes are an empty string + Assertions.assertEquals("", formattedNotes); + } + + /** + * Test the {@link ComponentInfoInventoryProcessor#formatTraceabilityNotes(ComponentInfo)} method with a mock + * ComponentInfo containing null for traceability notes. + */ + @Test + public void testFormatTraceabilityNotesWithNull() { + + // Create a mock ComponentInfo with an empty list of traceability notes + ComponentInfo componentInfo = Mockito.mock(ComponentInfo.class); + Mockito.when(componentInfo.getTraceabilityNotes()).thenReturn(null); + + // Create an instance of ComponentInfoInventoryProcessor + ComponentInfoInventoryProcessor processor = new ComponentInfoInventoryProcessor(); + + // Call the formatTraceabilityNotes method with the mocked ComponentInfo + String formattedNotes = processor.formatTraceabilityNotes(componentInfo); + + // Assert that the formatted notes are an empty string + Assertions.assertEquals("", formattedNotes); + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/model/impl/ApplicationComponentImplTest.java b/core/src/test/java/com/devonfw/tools/solicitor/model/impl/ApplicationComponentImplTest.java new file mode 100644 index 00000000..f6ad05e4 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/model/impl/ApplicationComponentImplTest.java @@ -0,0 +1,56 @@ +package com.devonfw.tools.solicitor.model.impl; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.solicitor.model.impl.inventory.ApplicationComponentImpl; + +/** + * This class contains JUnit test methods for the {@link ApplicationComponentImpl} class. + */ +class ApplicationComponentImplTest { + private ApplicationComponentImpl component; + + /** + * Initializes the {@code component} object before each test. + */ + @BeforeEach + public void setUp() { + + this.component = new ApplicationComponentImpl(); + } + + /** + * Tests the {@link ApplicationComponentImpl#setDataStatus(String)} and + * {@link ApplicationComponentImpl#getDataStatus()} methods. + */ + @Test + public void testDataStatus() { + + // Set dataStatus + String dataStatus = "VALID"; + this.component.setDataStatus(dataStatus); + + // Get dataStatus and assert + String retrievedDataStatus = this.component.getDataStatus(); + Assertions.assertEquals(dataStatus, retrievedDataStatus); + } + + /** + * Tests the {@link ApplicationComponentImpl#setTraceabilityNotes(String)} and + * {@link ApplicationComponentImpl#getTraceabilityNotes()} methods. + */ + @Test + public void testTraceabilityNotes() { + + // Set traceabilityNotes + String traceabilityNotes = "Note 1, Note 2, Note 3"; + this.component.setTraceabilityNotes(traceabilityNotes); + + // Get traceabilityNotes and assert + String retrievedTraceabilityNotes = this.component.getTraceabilityNotes(); + Assertions.assertEquals(traceabilityNotes, retrievedTraceabilityNotes); + } + +} diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 69a3849d..78579af2 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -155,6 +155,8 @@ The internal business data model consists of 6 entities: | copyrights | String | Copyright statements found in the components metadata / code (optional, see <>) | packageDownloadUrl | String | URL for downloading the component (optional, see <>) | sourceDownloadUrl | String | URL for downloading the sources of the component (optional, see <>) +| dataStatus | String | Optional status of the data associated with the component. Possible values TBD. +| traceabilityNotes | String | Optional notes for tracing the information about this component back to its origin. |=== ==== RawLicense @@ -1576,6 +1578,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.12.0:: +* https://github.com/devonfw/solicitor/issues/182: Add new data quality and traceability attributes to the ComponentInfo data structure. Changes in 1.11.0:: * https://github.com/devonfw/solicitor/pull/179: Added further name mapping rules. From 1d15b7a5d6a7bb9865d24b181e8fdcc464ca1235 Mon Sep 17 00:00:00 2001 From: duph97 Date: Wed, 2 Aug 2023 22:40:37 +0200 Subject: [PATCH 054/139] Simple report order fix (#186) --- .../devonfw/tools/solicitor/sql/allden_normalizedlicenses.sql | 3 ++- documentation/master-solicitor.asciidoc | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/allden_normalizedlicenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/allden_normalizedlicenses.sql index 86417bfb..6234f2b8 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/allden_normalizedlicenses.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/allden_normalizedlicenses.sql @@ -25,4 +25,5 @@ order by UPPER("artifactId"), UPPER("version"), UPPER("effectiveNormalizedLicense"), - UPPER("normalizedLicense") \ No newline at end of file + UPPER("normalizedLicense"), + UPPER("declaredLicense") \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 78579af2..62a5a82a 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1579,6 +1579,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.12.0:: * https://github.com/devonfw/solicitor/issues/182: Add new data quality and traceability attributes to the ComponentInfo data structure. +* https://github.com/devonfw/solicitor/issues/185: Fixed random ordering of rows in OSS-Inventory-Simple, which causes false differences to be marked. Changes in 1.11.0:: * https://github.com/devonfw/solicitor/pull/179: Added further name mapping rules. From 1569c3a578c5e4082da99ca7d6946ea1f5476e50 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 2 Aug 2023 22:49:14 +0200 Subject: [PATCH 055/139] Version 1.12.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index a0b970e2..7a933ddf 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.12.0-SNAPSHOT + 1.12.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index ec4a3555..07c27855 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.12.0-SNAPSHOT + 1.12.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 2a88f761..5e22d110 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.12.0-SNAPSHOT + 1.12.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index f963744d..0cf33326 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.12.0-SNAPSHOT + 1.12.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index dcb0f1f0..c26357de 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.12.0-SNAPSHOT + 1.12.0 pom Solicitor Aggregator From 41bb1c20c484a152b40336345be913f306e18bb9 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 2 Aug 2023 22:59:01 +0200 Subject: [PATCH 056/139] Set version to SNAPSHOT of next minor release --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 7a933ddf..55b93658 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.12.0 + 1.13.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 07c27855..34679f78 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.12.0 + 1.13.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 62a5a82a..666640c6 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1577,6 +1577,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.13.0:: + Changes in 1.12.0:: * https://github.com/devonfw/solicitor/issues/182: Add new data quality and traceability attributes to the ComponentInfo data structure. * https://github.com/devonfw/solicitor/issues/185: Fixed random ordering of rows in OSS-Inventory-Simple, which causes false differences to be marked. diff --git a/documentation/pom.xml b/documentation/pom.xml index 5e22d110..13b9d1b5 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.12.0 + 1.13.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 0cf33326..8348ccc5 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.12.0 + 1.13.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index c26357de..f98c1098 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.12.0 + 1.13.0-SNAPSHOT pom Solicitor Aggregator From dda219898babe4260d352fb9c02ad982882d7668 Mon Sep 17 00:00:00 2001 From: duph97 Date: Fri, 4 Aug 2023 13:09:09 +0200 Subject: [PATCH 057/139] Add missing license texts for SAX-PD and W3C (#188) * Add missing license texts for SAX-PD and W3C * wrap long lines in licenseinfo document --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../http___www_saxproject_org_copying_html | 26 +++++++++++++++++++ ...DOM_Level_3_Core_20040407_java_binding_zip | 18 +++++++++++++ .../Solicitor_LicenseInfo_Template.vm | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 core/src/test/resources/licenses/http___www_saxproject_org_copying_html create mode 100644 core/src/test/resources/licenses/http___www_w3_org_TR_2004_REC_DOM_Level_3_Core_20040407_java_binding_zip diff --git a/core/src/test/resources/licenses/http___www_saxproject_org_copying_html b/core/src/test/resources/licenses/http___www_saxproject_org_copying_html new file mode 100644 index 00000000..07e637e7 --- /dev/null +++ b/core/src/test/resources/licenses/http___www_saxproject_org_copying_html @@ -0,0 +1,26 @@ +Copyright Status +SAX is free! + +In fact, it's not possible to own a license to SAX, since it's been placed in the public domain. + +No Warranty +Because SAX is released to the public domain, there is no warranty for the design or for the software implementation, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide SAX "as is" without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of SAX is with you. Should SAX prove defective, you assume the cost of all necessary servicing, repair or correction. + +In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute SAX, be liable to you for damages, including any general, special, incidental or consequential damages arising out of the use or inability to use SAX (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the SAX to operate with any other programs), even if such holder or other party has been advised of the possibility of such damages. + +Copyright Disclaimers +This page includes statements to that effect by David Megginson, who would have been able to claim copyright for the original work. + +SAX 1.0 +Version 1.0 of the Simple API for XML (SAX), created collectively by the membership of the XML-DEV mailing list, is hereby released into the public domain. + +No one owns SAX: you may use it freely in both commercial and non-commercial applications, bundle it with your software distribution, include it on a CD-ROM, list the source code in a book, mirror the documentation at your own web site, or use it in any other way you see fit. + +David Megginson, Megginson Technologies Ltd. +1998-05-11 + +SAX 2.0 +I hereby abandon any property rights to SAX 2.0 (the Simple API for XML), and release all of the SAX 2.0 source code, compiled code, and documentation contained in this distribution into the Public Domain. SAX comes with NO WARRANTY or guarantee of fitness for any purpose. + +David Megginson, Megginson Technologies Ltd. +2000-05-05 \ No newline at end of file diff --git a/core/src/test/resources/licenses/http___www_w3_org_TR_2004_REC_DOM_Level_3_Core_20040407_java_binding_zip b/core/src/test/resources/licenses/http___www_w3_org_TR_2004_REC_DOM_Level_3_Core_20040407_java_binding_zip new file mode 100644 index 00000000..d7ee0a73 --- /dev/null +++ b/core/src/test/resources/licenses/http___www_w3_org_TR_2004_REC_DOM_Level_3_Core_20040407_java_binding_zip @@ -0,0 +1,18 @@ +W3C SOFTWARE NOTICE AND LICENSE +Copyright © 2004 World Wide Web Consortium, (Massachusetts Institute of Technology, European Research Consortium for Informatics and Mathematics, Keio University). All Rights Reserved. +The DOM bindings are published under the W3C Software Copyright Notice and License. The software license requires "Notice of any changes or modifications to the W3C files, including the date changes were made." Consequently, modified versions of the DOM bindings must document that they do not conform to the W3C standard; in the case of the IDL definitions, the pragma prefix can no longer be 'w3c.org'; in the case of the Java language binding, the package names can no longer be in the 'org.w3c' package. + +Note: The original version of the W3C Software Copyright Notice and License could be found at http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + +This work (and included software, documentation such as READMEs, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions. + +Permission to copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications: + +The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. +Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software Short Notice should be included (hypertext is preferred, text is permitted) within the body of any redistributed or derivative code. +Notice of any changes or modifications to the files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.) +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders. \ No newline at end of file diff --git a/core/src/test/resources/ownlicenseinfo/Solicitor_LicenseInfo_Template.vm b/core/src/test/resources/ownlicenseinfo/Solicitor_LicenseInfo_Template.vm index c9a578a8..83de0f00 100644 --- a/core/src/test/resources/ownlicenseinfo/Solicitor_LicenseInfo_Template.vm +++ b/core/src/test/resources/ownlicenseinfo/Solicitor_LicenseInfo_Template.vm @@ -113,7 +113,7 @@ are at least partly licensed via the following license of type $nl: #if ($nlc)
        -
        $esc.html($nlc)
        +
        $esc.html($esc.wrap100to80($nlc))
        #else From fe27856861a05727ad172282eef2fe7e890f79a9 Mon Sep 17 00:00:00 2001 From: duph97 Date: Tue, 8 Aug 2023 15:03:22 +0200 Subject: [PATCH 058/139] CycloneDX Reader (#184) Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- core/pom.xml | 1 - .../tools/solicitor/common/LogMessages.java | 4 +- .../reader/cyclonedx/CyclonedxReader.java | 154 + .../cyclonedx/CyclonedxReaderTests.java | 188 + core/src/test/resources/mavensbom.json | 1621 ++++++++ core/src/test/resources/npmsbom.json | 3551 +++++++++++++++++ documentation/master-solicitor.asciidoc | 49 +- solicitor.dict | 3 + 8 files changed, 5563 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java create mode 100644 core/src/test/resources/mavensbom.json create mode 100644 core/src/test/resources/npmsbom.json diff --git a/core/pom.xml b/core/pom.xml index 34679f78..4965ec33 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -170,7 +170,6 @@ pdf provided - diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 010d663b..67d9aa15 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -86,7 +86,9 @@ public enum LogMessages { CURATIONS_PROCESSING(58, "Curations file '{}' exists. Applying curations."), // COMPONENTINFO_NO_LICENSES(59, "ComponentInfo for '{}' does not contain any license. Keeping licenses from Reader."), // CLASSPATHEXCEPTION_WITHOUT_GPL(60, "ClassPathException was found but no GPL License exists for {}"), // - CLASSPATHEXCEPTION_MULTIPLE_GPL(61, "ClassPathException was found but there are multiple GPL Licenses for {}"); + CLASSPATHEXCEPTION_MULTIPLE_GPL(61, "ClassPathException was found but there are multiple GPL Licenses for {}"), // + CYCLONEDX_UNSUPPORTED_PURL(62, + "The CycloneDX file contains the PackageURL '{}' with unsupported type which will be ignored. Solicitor reports might be incomplete."); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java new file mode 100644 index 00000000..bf6a8e1c --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java @@ -0,0 +1,154 @@ +package com.devonfw.tools.solicitor.reader.cyclonedx; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.common.packageurl.SolicitorPackageURLException; +import com.devonfw.tools.solicitor.common.packageurl.impl.DelegatingPackageURLHandlerImpl; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; +import com.devonfw.tools.solicitor.reader.AbstractReader; +import com.devonfw.tools.solicitor.reader.Reader; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * A {@link Reader} which reads data produced by the CDXGEN Tool. + */ +@Component +public class CyclonedxReader extends AbstractReader implements Reader { + + private static final Logger LOG = LoggerFactory.getLogger(CyclonedxReader.class); + + /** + * The supported type of this {@link Reader}. + */ + public static final String SUPPORTED_TYPE = "cyclonedx"; + + @Autowired + private DelegatingPackageURLHandlerImpl delegatingPackageURLHandler; + + public void setDelegatingPackageURLHandler(DelegatingPackageURLHandlerImpl delegatingPackageURLHandler) { + + this.delegatingPackageURLHandler = delegatingPackageURLHandler; + } + + /** {@inheritDoc} */ + @Override + public Set getSupportedTypes() { + + return Collections.singleton(SUPPORTED_TYPE); + } + + /** {@inheritDoc} */ + @Override + public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, + String repoType, Map configuration) { + + int componentCount = 0; + int licenseCount = 0; + InputStream is; + try { + is = this.inputStreamFactory.createInputStreamFor(sourceUrl); + } catch (IOException e1) { + throw new SolicitorRuntimeException("Could not open inventory source '" + sourceUrl + "' for reading", e1); + } + // According to tutorial https://github.com/FasterXML/jackson-databind/ + ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + try { + JsonNode rootNode = mapper.readTree(is); + + // Get all components in SBOM + JsonNode componentsNode = rootNode.get("components"); + if (componentsNode != null) { + for (JsonNode componentNode : componentsNode) { + + String groupId = componentNode.get("group").asText(); + String artifactId = componentNode.get("name").asText(); + String version = componentNode.get("version").asText(); + String purl = componentNode.get("purl").asText(); + + // Fill appComponents + ApplicationComponent appComponent = getModelFactory().newApplicationComponent(); + appComponent.setApplication(application); + componentCount++; + + appComponent.setGroupId(groupId); + appComponent.setArtifactId(artifactId); + appComponent.setVersion(version); + appComponent.setUsagePattern(usagePattern); + appComponent.setRepoType(repoType); + + // Fill purl + try { + // check if handler exists for the package type defined in purl + if (!this.delegatingPackageURLHandler.sourceDownloadUrlFor(purl).isEmpty()) { + appComponent.setPackageUrl(purl); + } + } catch (SolicitorPackageURLException ex) { + if (LOG.isDebugEnabled()) { + LOG.debug("Problem with PackageURL", ex); + } + LOG.warn(LogMessages.CYCLONEDX_UNSUPPORTED_PURL.msg(), purl); + } + + // Fill license information + JsonNode licensesNode = componentNode.get("licenses"); // licenses + + // Case if no licenses field exists + if (licensesNode == null) { + addRawLicense(appComponent, null, null, sourceUrl); + } + // Case if licenses field exists but is empty + else if (licensesNode != null && licensesNode.isEmpty()) { + addRawLicense(appComponent, null, null, sourceUrl); + } + // Case if licenses field exists and contains 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); + } + } + } + } + } + } + doLogging(sourceUrl, application, componentCount, licenseCount); + } catch (IOException e) { + throw new SolicitorRuntimeException("Could not read CycloneDx inventory source '" + sourceUrl + "'", e); + } + + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java new file mode 100644 index 00000000..560f436e --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java @@ -0,0 +1,188 @@ + +package com.devonfw.tools.solicitor.reader.cyclonedx; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.tools.solicitor.common.FileInputStreamFactory; +import com.devonfw.tools.solicitor.common.packageurl.SolicitorPackageURLException; +import com.devonfw.tools.solicitor.common.packageurl.impl.DelegatingPackageURLHandlerImpl; +import com.devonfw.tools.solicitor.model.ModelFactory; +import com.devonfw.tools.solicitor.model.impl.ModelFactoryImpl; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.inventory.RawLicense; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; + +/** + * This class contains JUnit test methods for the {@link CyclonedxReader} class. + */ +public class CyclonedxReaderTests { + private static final Logger LOG = LoggerFactory.getLogger(CyclonedxReaderTests.class); + + CyclonedxReader cdxr = new CyclonedxReader(); + + ModelFactory modelFactory = new ModelFactoryImpl(); + + // Create Mock for DelegatingPackageURLHandlerImpl + DelegatingPackageURLHandlerImpl delegatingPurlHandler = Mockito.mock(DelegatingPackageURLHandlerImpl.class); + + /** + * Test the {@link CyclonedxReader#readInventory()} method. Input file is an SBOM containing maven components. Mock + * the case, that a maven PurlHandler exists. + */ + @Test + public void readMavenFileAndCheckSize() { + + // Always return a non-empty String for maven purls + Mockito.when(this.delegatingPurlHandler.sourceDownloadUrlFor(Mockito.startsWith("pkg:maven/"))).thenReturn("foo"); + + Application application = this.modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com", + "Java8"); + this.cdxr.setModelFactory(this.modelFactory); + this.cdxr.setInputStreamFactory(new FileInputStreamFactory()); + this.cdxr.setDelegatingPackageURLHandler(this.delegatingPurlHandler); + this.cdxr.readInventory("maven", "src/test/resources/mavensbom.json", application, UsagePattern.DYNAMIC_LINKING, + "cyclonedx", null); + LOG.info(application.toString()); + + assertEquals(32, application.getApplicationComponents().size()); + + boolean found = false; + for (ApplicationComponent ap : application.getApplicationComponents()) { + if (ap.getGroupId().equals("org.springframework.boot") && // + ap.getArtifactId().equals("spring-boot-starter-logging") && // + ap.getVersion().equals("2.3.3.RELEASE")) { + found = true; + assertEquals("pkg:maven/org.springframework.boot/spring-boot-starter-logging@2.3.3.RELEASE?type=jar", + ap.getPackageUrl()); + break; + } + } + assertTrue(found); + } + + /** + * Test the {@link CyclonedxReader#readInventory()} method. Input file is an SBOM containing maven components. Mock + * the case, that a maven PurlHandler does not exist. + */ + @Test + public void readMavenFileAndCheckSizeNegative() { + + // Always throw exception for maven purls + Mockito.when(this.delegatingPurlHandler.sourceDownloadUrlFor(Mockito.startsWith("pkg:maven/"))).thenThrow( + new SolicitorPackageURLException("No applicable SingleKindPackageURLHandler found for type 'maven'")); + + Application application = this.modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com", + "Java8"); + this.cdxr.setModelFactory(this.modelFactory); + this.cdxr.setInputStreamFactory(new FileInputStreamFactory()); + this.cdxr.setDelegatingPackageURLHandler(this.delegatingPurlHandler); + this.cdxr.readInventory("maven", "src/test/resources/mavensbom.json", application, UsagePattern.DYNAMIC_LINKING, + "cyclonedx", null); + LOG.info(application.toString()); + + assertEquals(32, application.getApplicationComponents().size()); + + } + + /** + * Test the {@link CyclonedxReader#readInventory()} method. Input file is an SBOM containing maven components. Check + * one entry for correct content. + */ + @Test + public void readMavenFileAndCheckSingleContentSize() { + + // Always return a non-empty String for maven purls + Mockito.when(this.delegatingPurlHandler.sourceDownloadUrlFor(Mockito.startsWith("pkg:maven/"))).thenReturn("foo"); + + Application application = this.modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com", + "Java8"); + this.cdxr.setModelFactory(this.modelFactory); + this.cdxr.setInputStreamFactory(new FileInputStreamFactory()); + this.cdxr.setDelegatingPackageURLHandler(this.delegatingPurlHandler); + this.cdxr.readInventory("maven", "src/test/resources/mavensbom.json", application, UsagePattern.DYNAMIC_LINKING, + "someRepoType", null); + LOG.info(application.toString()); + + assertEquals(32, application.getApplicationComponents().size()); + + boolean found = false; + for (ApplicationComponent ap : application.getApplicationComponents()) { + if (ap.getGroupId().equals("ch.qos.logback") && // + ap.getArtifactId().equals("logback-classic") && // + ap.getVersion().equals("1.2.3")) { + found = true; + assertEquals(UsagePattern.DYNAMIC_LINKING, ap.getUsagePattern()); + assertEquals("someRepoType", ap.getRepoType()); + assertEquals("pkg:maven/ch.qos.logback/logback-classic@1.2.3?type=jar", ap.getPackageUrl()); + assertEquals(4, ap.getRawLicenses().size()); + boolean l1found = false; + boolean l2found = false; + boolean l3found = false; + boolean l4found = false; + + for (RawLicense rl : ap.getRawLicenses()) { + if (rl.getDeclaredLicense().equals("EPL-1.0")) { + l1found = true; + assertNull(rl.getLicenseUrl()); + } + if (rl.getDeclaredLicense().equals("EPL-2.0")) { + l2found = true; + assertEquals("some url", rl.getLicenseUrl()); + } + if (rl.getDeclaredLicense().equals("someOtherLicense")) { + l3found = true; + assertNull(rl.getLicenseUrl()); + } + if (rl.getDeclaredLicense().equals("GNU Lesser General Public License")) { + l4found = true; + assertEquals("http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", rl.getLicenseUrl()); + + } + } + assertTrue(l1found && l2found && l3found && l4found); + break; + } + } + assertTrue(found); + } + + /** + * Test the {@link CyclonedxReader#readInventory()} method. Input file is an SBOM containing npm components. Mock the + * case, that a npm PurlHandler exists. + */ + @Test + public void readNpmFileAndCheckSize() { + + // Always return a non-empty String for npm purls + Mockito.when(this.delegatingPurlHandler.sourceDownloadUrlFor(Mockito.startsWith("pkg:npm/"))).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/npmsbom.json", application, UsagePattern.DYNAMIC_LINKING, + "cyclonedx", null); + LOG.info(application.toString()); + + assertEquals(74, application.getApplicationComponents().size()); + + boolean found = false; + for (ApplicationComponent ap : application.getApplicationComponents()) { + if (ap.getArtifactId().equals("async") && ap.getVersion().equals("3.2.4")) { + found = true; + assertEquals("pkg:npm/async@3.2.4", ap.getPackageUrl()); + break; + } + } + assertTrue(found); + } +} diff --git a/core/src/test/resources/mavensbom.json b/core/src/test/resources/mavensbom.json new file mode 100644 index 00000000..991cd759 --- /dev/null +++ b/core/src/test/resources/mavensbom.json @@ -0,0 +1,1621 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "serialNumber": "urn:uuid:4d16ed63-3de0-4530-b439-9afde82a0563", + "version": 1, + "metadata": { + "timestamp": "2023-06-27T14:27:19.300Z", + "tools": [ + { + "vendor": "cyclonedx", + "name": "cdxgen", + "version": "8.6.0" + } + ], + "authors": [ + { + "name": "Prabhu Subramanian", + "email": "prabhu@appthreat.com" + } + ], + "component": { + "author": "", + "publisher": "", + "group": "some.name", + "name": "hello-world", + "version": "0.0.1-SNAPSHOT", + "description": "Hello World WebApp in Spring Boot", + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/some.name/hello-world@0.0.1-SNAPSHOT?type=jar", + "type": "library", + "bom-ref": "pkg:maven/some.name/hello-world@0.0.1-SNAPSHOT?type=jar" + } + }, + "components": [ + { + "author": "", + "publisher": "Pivotal Software, Inc.", + "group": "org.springframework.boot", + "name": "spring-boot-starter-web", + "version": "2.3.3.RELEASE", + "description": "Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "9a252be8e1dbea48b5246750ddd3fc91" + }, + { + "alg": "SHA-1", + "content": "d38db3c19ba4bc114aaa4febfc1d89cd8725822d" + }, + { + "alg": "SHA-256", + "content": "b8addf622b682fdd9c873690274dd9bbe7e8d2220fae0205ffabc78e71eafc8a" + }, + { + "alg": "SHA-512", + "content": "dd62ddd56eb6bc6483de2f3324bf9e97bed3dc45e4955685d7629ec2e75479636bb94734690adceef4228a7be23b28a753d2af19719320f8a651d94ea7f91524" + }, + { + "alg": "SHA-384", + "content": "c63b37925bbf5a14eff2472a50aa0bd7c8a7390a119b74d30074cb3e5cdcbe18363a7760e2c1c762a815bf917986835a" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework.boot/spring-boot-starter-web@2.3.3.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework.boot/spring-boot-starter-web@2.3.3.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Pivotal Software, Inc.", + "group": "org.springframework.boot", + "name": "spring-boot-starter", + "version": "2.3.3.RELEASE", + "description": "Core starter, including auto-configuration support, logging and YAML", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "dd1db9b502fd93399a05a588d110e373" + }, + { + "alg": "SHA-1", + "content": "c9bb3464e83990465b86fb94da3d20f92f036d1e" + }, + { + "alg": "SHA-256", + "content": "be19878791765c8aaff208465edd45c12292b10739f425beb1612d2fa50145ee" + }, + { + "alg": "SHA-512", + "content": "9c05cd37b08b95c16744c48473f6bda115656ea5bf8663aae0d34337b2084d3c6daa925769baf4703c7f9b9089cb8758b92b97b501964603a58e5366b7ab2ebc" + }, + { + "alg": "SHA-384", + "content": "987d1c31a382b0eaedcf1b37b238e593af2891a3f8681060130b7147d37c29ddef63f59ceaf64edc81329159e85e57ef" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework.boot/spring-boot-starter@2.3.3.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework.boot/spring-boot-starter@2.3.3.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Pivotal Software, Inc.", + "group": "org.springframework.boot", + "name": "spring-boot", + "version": "2.3.3.RELEASE", + "description": "Spring Boot", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "ee84c2c09e1b3ffff93abc4df3a65fc4" + }, + { + "alg": "SHA-1", + "content": "74f0cfae433a6ba6d936c61baa5a5316a688dc22" + }, + { + "alg": "SHA-256", + "content": "073b052a71e66d01e8ee885fb8c2c968dc9a321fcb363de1da793d0fe4294c1f" + }, + { + "alg": "SHA-512", + "content": "1d2cd7464c4914fd7989c7f0d99b9e2d6c37173682a71bb72b59f390fac20c7c32d0bf719927ff58f761199443e82e79c0066680236c7738c7ca84e4deb207d0" + }, + { + "alg": "SHA-384", + "content": "c1252967a4ffd207a4eca44bb336a96f4f69a470b247232a2a67b4273bdb5d07b9a0f0eafd963c05c5fc5d8f1d31e2aa" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework.boot/spring-boot@2.3.3.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework.boot/spring-boot@2.3.3.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Pivotal Software, Inc.", + "group": "org.springframework.boot", + "name": "spring-boot-autoconfigure", + "version": "2.3.3.RELEASE", + "description": "Spring Boot AutoConfigure", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "08a2c602b0c04755fcd1e5ab9fc2d698" + }, + { + "alg": "SHA-1", + "content": "a1343e09cb6024bb4fbf656ce3cd3d13f12ccbdd" + }, + { + "alg": "SHA-256", + "content": "2c9bfcfedc7715b53308178adff84d9678484a5b4337363e35f75296fcef68df" + }, + { + "alg": "SHA-512", + "content": "4274bb37c0d72a4999d2ae8264ce789cb4e6ca553ccd830a974fddf91e80ec9ba5ff0f895d54ad5bef03699d4b5cacdb736dcc672e0cbccc2f51950d7d6ce8b6" + }, + { + "alg": "SHA-384", + "content": "b358fc6bc94996b5cb26e450702ce2873bd23ceb4e23d2c51d14c234842c1028b62a5b4e78a342816573b2027f90e621" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework.boot/spring-boot-autoconfigure@2.3.3.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework.boot/spring-boot-autoconfigure@2.3.3.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Pivotal Software, Inc.", + "group": "org.springframework.boot", + "name": "spring-boot-starter-logging", + "version": "2.3.3.RELEASE", + "description": "Starter for logging using Logback. Default logging starter", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "029b59f87319f56152150bb3285826c4" + }, + { + "alg": "SHA-1", + "content": "23523992dd7378fa565b1fc70e19629e8b867ada" + }, + { + "alg": "SHA-256", + "content": "1489977df0ffa52aff8ebffeed579b52385f02cbba916e7b1c8d0335a9b5cd33" + }, + { + "alg": "SHA-512", + "content": "f6a23f9654685d9e84188dbd4a1519be0af4c292774a930081224ce8edaf202846ede29afc2803ca1d4d8eee342dae94bbf353e6396e87a8f9d880fc0a9029c0" + }, + { + "alg": "SHA-384", + "content": "9b8eeecba540a7d96d992285e9543bad5af1c8dec52b05d68a5a1b0e11abee71aa41debe83952414adf82a9cb2043e73" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework.boot/spring-boot-starter-logging@2.3.3.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework.boot/spring-boot-starter-logging@2.3.3.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "QOS.ch", + "group": "ch.qos.logback", + "name": "logback-classic", + "version": "1.2.3", + "description": "logback-classic module", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "64f7a68f931aed8e5ad8243470440f0b" + }, + { + "alg": "SHA-1", + "content": "7c4f3c474fb2c041d8028740440937705ebb473a" + }, + { + "alg": "SHA-256", + "content": "fb53f8539e7fcb8f093a56e138112056ec1dc809ebb020b59d8a36a5ebac37e0" + }, + { + "alg": "SHA-512", + "content": "9ad5df9055e74c1db67e10422774e740903477c821591702d2709a4c1f73e3fc3fa6b1a871b6985901817bc2bdeba916849035dc2bbf518f308637b0586e36f1" + }, + { + "alg": "SHA-384", + "content": "42a5975c9db68045fd2d662f5168df6b19e5e8b715401fd5786325a02f531b44f1cf4e64f69f5ef5bbef70929e1d6fd3" + } + ], + "licenses": [ + { + "license": { + "id": "EPL-1.0", + "name": "name to be ignored as id takes precedence" + } + }, + { + "license": { + "id": "EPL-2.0", + "name": "another name to be ignored as id takes precedence", + "url": "some url" + + } + }, + { + "license": { + "name": "someOtherLicense" + } + }, + { + "license": { + "name": "GNU Lesser General Public License", + "url": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" + } + } + ], + "purl": "pkg:maven/ch.qos.logback/logback-classic@1.2.3?type=jar", + "type": "library", + "bom-ref": "pkg:maven/ch.qos.logback/logback-classic@1.2.3?type=jar" + }, + { + "author": "", + "publisher": "QOS.ch", + "group": "ch.qos.logback", + "name": "logback-core", + "version": "1.2.3", + "description": "logback-core module", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "841fc80c6edff60d947a3872a2db4d45" + }, + { + "alg": "SHA-1", + "content": "864344400c3d4d92dfeb0a305dc87d953677c03c" + }, + { + "alg": "SHA-256", + "content": "5946d837fe6f960c02a53eda7a6926ecc3c758bbdd69aa453ee429f858217f22" + }, + { + "alg": "SHA-512", + "content": "bd1a7512647fe61b90cfd18bedf2a33f3f16f334f8f8ce947cdd353c0b0b7a7cce203070f0d2183f6583e0f2b2fe6e0b12eb93bd5b2dc29076e7b466447f6dc5" + }, + { + "alg": "SHA-384", + "content": "2c34d2bc4c85beee3d82b7aff9f351615a1de9a4f1e262c6e891136a621a3ea27296fbac399398aa8c1fd857fa38393d" + } + ], + "licenses": [ + { + "license": { + "id": "EPL-1.0" + } + }, + { + "license": { + "name": "GNU Lesser General Public License", + "url": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" + } + } + ], + "purl": "pkg:maven/ch.qos.logback/logback-core@1.2.3?type=jar", + "type": "library", + "bom-ref": "pkg:maven/ch.qos.logback/logback-core@1.2.3?type=jar" + }, + { + "author": "", + "publisher": "QOS.ch", + "group": "org.slf4j", + "name": "slf4j-api", + "version": "1.7.30", + "description": "The slf4j API", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "f8be00da99bc4ab64c79ab1e2be7cb7c" + }, + { + "alg": "SHA-1", + "content": "b5a4b6d16ab13e34a88fae84c35cd5d68cac922c" + }, + { + "alg": "SHA-256", + "content": "cdba07964d1bb40a0761485c6b1e8c2f8fd9eb1d19c53928ac0d7f9510105c57" + }, + { + "alg": "SHA-512", + "content": "e5435852569dda596ba46138af8ee9c4ecba8a7a43f4f1e7897aeb4430523a0f037088a7b63877df5734578f19d331f03d7b0f32d5ae6c425df211947b3e6173" + }, + { + "alg": "SHA-384", + "content": "d8061c2f86f33b813cb86d3d54c615d26aa89afb43cabf8d828ff6a3f4f8e63d4a3b27355033f67ba60c2cdf2558d9de" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:maven/org.slf4j/slf4j-api@1.7.30?type=jar", + "type": "library", + "bom-ref": "pkg:maven/org.slf4j/slf4j-api@1.7.30?type=jar" + }, + { + "author": "", + "publisher": "The Apache Software Foundation", + "group": "org.apache.logging.log4j", + "name": "log4j-to-slf4j", + "version": "2.13.3", + "description": "The Apache Log4j binding between Log4j 2 API and SLF4J.", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "bafb53f0385a82d4f9c1145917908736" + }, + { + "alg": "SHA-1", + "content": "966f6fd1af4959d6b12bfa880121d4a2b164f857" + }, + { + "alg": "SHA-256", + "content": "9624e9aaf60b1875adde33d8e7997de110b70be09e94e55ad8fc39637ec002c4" + }, + { + "alg": "SHA-512", + "content": "8a6c12420857d63e4986cbbd6d0c65cc91fe295814d097f511bd87f422228ebd36f7d1016a3122570749d318d6f429bcbf63fec98a64430453db6a12b7633ef6" + }, + { + "alg": "SHA-384", + "content": "722c79a4ec8d6074041eccbe623acb95bdcd21b105943001a397ad4cf36e71944ca1f35576e83078d3fd2e87427d5fdc" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.apache.logging.log4j/log4j-to-slf4j@2.13.3?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.apache.logging.log4j/log4j-to-slf4j@2.13.3?type=jar" + }, + { + "author": "", + "publisher": "The Apache Software Foundation", + "group": "org.apache.logging.log4j", + "name": "log4j-api", + "version": "2.13.3", + "description": "The Apache Log4j API", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "236b9969df6b394e88283a9f813b9b95" + }, + { + "alg": "SHA-1", + "content": "ec1508160b93d274b1add34419b897bae84c6ca9" + }, + { + "alg": "SHA-256", + "content": "2b4b1965c9dce7f3732a0fbf5c8493199c1e6bf8cf65c3e235b57d98da5f36af" + }, + { + "alg": "SHA-512", + "content": "468ad08aa891b5c92156849945eee3ab074ae8f4c760b9de9a4c15a70857ae0b98bafb425e6d673e9bd43efef009a8e0b3bee767feaa4fc2fa7564f30b0a8441" + }, + { + "alg": "SHA-384", + "content": "7189232d8a6432f6434df8f42295bf384b6c4d2031ec62c1dd794c59b2340d4f541694b0d2150a345756bfd4ef7d1949" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.apache.logging.log4j/log4j-api@2.13.3?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.apache.logging.log4j/log4j-api@2.13.3?type=jar" + }, + { + "author": "", + "publisher": "QOS.ch", + "group": "org.slf4j", + "name": "jul-to-slf4j", + "version": "1.7.30", + "description": "JUL to SLF4J bridge", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "f2c78cb93d70dc5dea0c50f36ace09c1" + }, + { + "alg": "SHA-1", + "content": "d58bebff8cbf70ff52b59208586095f467656c30" + }, + { + "alg": "SHA-256", + "content": "bbcbfdaa72572255c4f85207a9bfdb24358dc993e41252331bd4d0913e4988b9" + }, + { + "alg": "SHA-512", + "content": "82d77af8c4db84d6b35b8138dc73e6d8c5ebbc6907f77295627d8402a5c63aa3fea05bfc0f8982b3a2120615d8edc4fcb947468c1de3dd33cf943690737e652d" + }, + { + "alg": "SHA-384", + "content": "12ecfcb3a8fd8d83f485253eab2bc3933b12cdaa80d99269232d5b98ff3bc0dc6b36ef76d6dfa13fe5207f4b5f25355f" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:maven/org.slf4j/jul-to-slf4j@1.7.30?type=jar", + "type": "library", + "bom-ref": "pkg:maven/org.slf4j/jul-to-slf4j@1.7.30?type=jar" + }, + { + "author": "", + "publisher": "Eclipse Foundation", + "group": "jakarta.annotation", + "name": "jakarta.annotation-api", + "version": "1.3.5", + "description": "Jakarta Annotations API", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "8b165cf58df5f8c2a222f637c0a07c97" + }, + { + "alg": "SHA-1", + "content": "59eb84ee0d616332ff44aba065f3888cf002cd2d" + }, + { + "alg": "SHA-256", + "content": "85fb03fc054cdf4efca8efd9b6712bbb418e1ab98241c4539c8585bbc23e1b8a" + }, + { + "alg": "SHA-512", + "content": "d1acff146c0f9ea923a9325ad4c22ba2052ec474341ab8392abab7e8abd3ca010db2400ff9b5849fc4f1fa5c0a18830eb104da07a13bd26b4f0a43d167935878" + }, + { + "alg": "SHA-384", + "content": "004a4bde333c0575f72df1cb9cf95ee0c6c7f738a6f0f723a5ec545aaa1664abeb82f01627708a1377e3136754fb7859" + } + ], + "licenses": [ + { + "license": { + "id": "EPL-2.0" + } + }, + { + "license": { + "id": "GPL-2.0-with-classpath-exception" + } + } + ], + "purl": "pkg:maven/jakarta.annotation/jakarta.annotation-api@1.3.5?type=jar", + "type": "library", + "bom-ref": "pkg:maven/jakarta.annotation/jakarta.annotation-api@1.3.5?type=jar" + }, + { + "author": "", + "publisher": "Spring IO", + "group": "org.springframework", + "name": "spring-core", + "version": "5.2.8.RELEASE", + "description": "Spring Core", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "85f36c62cc44ec8b6c8f7478005a0e9d" + }, + { + "alg": "SHA-1", + "content": "35a1654028254abd7cb85799a891de8a0ab9f599" + }, + { + "alg": "SHA-256", + "content": "fad4318d5a6850962d1975df6368b5050abb44d8b52f561783587da09bfcee7a" + }, + { + "alg": "SHA-512", + "content": "a3de6a3aef2d0be5400fe1faaf57c0eff174cc10c2de8b6c81f716132efae00be6de0e30b6bfc4c93fb84f6ff3d8cdbeea2fe7fd080a1c20a76a471bb74fd4ca" + }, + { + "alg": "SHA-384", + "content": "b8dd8987d052af3afbfffacb3c7c1398313361d8270593eb70af7f68900c45daf521e89173f73a751dda6b24f344a8eb" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Spring IO", + "group": "org.springframework", + "name": "spring-jcl", + "version": "5.2.8.RELEASE", + "description": "Spring Commons Logging Bridge", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "0c0b9dd5956f16f99f0f13393dc7da7a" + }, + { + "alg": "SHA-1", + "content": "e18b8dea088cc58fad8fc25ee93f22987dd05d94" + }, + { + "alg": "SHA-256", + "content": "afd2c19cfea5d0fb3fea6c672604b9b88b32b676e458ce3ebad1d1bbb73a0123" + }, + { + "alg": "SHA-512", + "content": "57cc84ccf4b3411a9c3be8931abc190eca3646a3a46d0a7e48b0b30c67ff66c5497bb5264b0d74110c4a87c04bbc2a373726cb23e40968ca9898bbc08bc8eeb5" + }, + { + "alg": "SHA-384", + "content": "318a7555e167ae601fa7d2a7a2b8643d36ffeaedc80d115c5c6fe4c03f39e1f7a52b8a6de0e99543642da7dc9bb14fc4" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework/spring-jcl@5.2.8.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework/spring-jcl@5.2.8.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "", + "group": "org.yaml", + "name": "snakeyaml", + "version": "1.26", + "description": "YAML 1.1 parser and emitter for Java", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "72d987f6193910b63c5e6881ab64da32" + }, + { + "alg": "SHA-1", + "content": "a78a8747147d2c5807683e76ec2b633e95c14fe9" + }, + { + "alg": "SHA-256", + "content": "d87d607e500885356c03c1cae61e8c2e05d697df8787d5aba13484c2eb76a844" + }, + { + "alg": "SHA-512", + "content": "dce610268daab6fd6bac54f819f2f5795b17a57792b0f5553ee72acfbe8b9f81367ba7419655be8348369eb489259b14ddab7cea7a0c3c1b193bd62e0715cfd3" + }, + { + "alg": "SHA-384", + "content": "0fa4b6459d02d0b2fe4dbfb59644b0f4dee3593a0993b66b218dfda0a66da81eb89bd56610e7baa459495ea18807eb41" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.yaml/snakeyaml@1.26?type=jar", + "type": "library", + "bom-ref": "pkg:maven/org.yaml/snakeyaml@1.26?type=jar" + }, + { + "author": "", + "publisher": "Pivotal Software, Inc.", + "group": "org.springframework.boot", + "name": "spring-boot-starter-json", + "version": "2.3.3.RELEASE", + "description": "Starter for reading and writing json", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "d0826134a0bd041c2621280b6eb76021" + }, + { + "alg": "SHA-1", + "content": "8c40b22a0635989ecc58f35d82fd55445be8822f" + }, + { + "alg": "SHA-256", + "content": "199bfdf78d4b3e32d79244718666f0879615312418b5cd0121573f224e057900" + }, + { + "alg": "SHA-512", + "content": "c2f97d3b997636b6c5847e9a2e0aa252424b918261f0f41050ba34b5b8c565160906d7599a09097f039b28dfc0ff820a18b1a0221e669f4736b2b7a671566e95" + }, + { + "alg": "SHA-384", + "content": "4227c1202e06491bc2bd3a8e5f1e745a5760a93cdfe390b240eac2d7d9671666933dd4fa442dfc447c629771e581a229" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework.boot/spring-boot-starter-json@2.3.3.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework.boot/spring-boot-starter-json@2.3.3.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "FasterXML", + "group": "com.fasterxml.jackson.core", + "name": "jackson-databind", + "version": "2.11.2", + "description": "General data-binding functionality for Jackson: works on core streaming API", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "14fd0effa4d1d3e09edb36423be82aff" + }, + { + "alg": "SHA-1", + "content": "ee08bbd8975dde844307fe8309dfcd5ec7ee129d" + }, + { + "alg": "SHA-256", + "content": "cb890b4aad8ed21a7b57e3c8f7924dbdca1aeff9ddd27cb0ff37243037ae1342" + }, + { + "alg": "SHA-512", + "content": "6d12007ec0b95964fc41478c823cd200e55d2c06da3784f3c095293e506beae7b2e4197287e69b0f34864aebfa22b285c76d1f14b4727dcbd2ab39e204eaaf36" + }, + { + "alg": "SHA-384", + "content": "c05ba05f6c74a51581eed49d41fc42a005d8e37624539183928ba00271635774138638384db6ee9fca5fe6bf304c15c3" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.11.2?type=jar", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.11.2?type=jar" + }, + { + "author": "", + "publisher": "FasterXML", + "group": "com.fasterxml.jackson.core", + "name": "jackson-annotations", + "version": "2.11.2", + "description": "Core annotations used for value types, used by Jackson data binding package.", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "fb3fd29c5c990b78c8d1b40b9e8839de" + }, + { + "alg": "SHA-1", + "content": "0e0a7f61fce3e3eac38a079c11831868269de2ea" + }, + { + "alg": "SHA-256", + "content": "90d602d1955df509b1569618cff869994caf9483cb82a3ccb39782a5cda54126" + }, + { + "alg": "SHA-512", + "content": "e66987067aa91874af0f8417489e92d55ac7d398bc6cdf6ee03647166a4219498e3b61c22df46bc7518f63fd66d9b24edf2419bd82096a9464e4cb85c8c90256" + }, + { + "alg": "SHA-384", + "content": "8957338e2cfcd54fd8145f29961863890da7f7fab589fa520a94d17e6e2a9d7cdae2e1a51e3a58fbbc87a1de613c6810" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.11.2?type=jar", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.11.2?type=jar" + }, + { + "author": "", + "publisher": "FasterXML", + "group": "com.fasterxml.jackson.core", + "name": "jackson-core", + "version": "2.11.2", + "description": "Core Jackson processing abstractions (aka Streaming API), implementation for JSON", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "1cf7bf96372fd34cb4a450f19d8e706b" + }, + { + "alg": "SHA-1", + "content": "bc022ab0f0c83c07f9c52c5ab9a6a4932b15cc35" + }, + { + "alg": "SHA-256", + "content": "f8d768c4e8884522be5881dd2a91aec812d08d4f05852b434190e22de659dfc9" + }, + { + "alg": "SHA-512", + "content": "e85f18e52b2ec8b6a92f4990b74ca3ee5150d79db8f9d50970a5a5744edcdff26a6cc45f8ed6f278af075ed1bc8b11f47ad51f2a3582a2f6bce243dcf1be114c" + }, + { + "alg": "SHA-384", + "content": "5385b78c5172d4917df59689b65ebef3ba07deb860f903faf1fb1258685727516951771d905a74ad353528bc7a7014ac" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.11.2?type=jar", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.11.2?type=jar" + }, + { + "author": "", + "publisher": "FasterXML", + "group": "com.fasterxml.jackson.datatype", + "name": "jackson-datatype-jdk8", + "version": "2.11.2", + "description": "Add-on module for Jackson (http://jackson.codehaus.org) to support JDK 8 data types.", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "a43cefe1a03a5a6f1967b5832873b123" + }, + { + "alg": "SHA-1", + "content": "d4c1933a8d62db65c3d5a5cd809511e021a189c0" + }, + { + "alg": "SHA-256", + "content": "fa585ff4aed2b250538dd42d53d263fc96c9b1c720e836214e443e4cf28af61f" + }, + { + "alg": "SHA-512", + "content": "91a8b82a15310a2a0402af0d5d885da4e985fbd8ef9f18805eca5ef9c23e763855d28dd34f69a81a3462bbe1c57b3de5aeed23d6b4127ecc587b0396c77da668" + }, + { + "alg": "SHA-384", + "content": "9c23df336557311923272d438d1aafb4138cdc85ede494a520a67fc403e07f8799830af11cab5153bf241afb2a40a9c2" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/com.fasterxml.jackson.datatype/jackson-datatype-jdk8@2.11.2?type=jar", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.datatype/jackson-datatype-jdk8@2.11.2?type=jar" + }, + { + "author": "", + "publisher": "FasterXML", + "group": "com.fasterxml.jackson.datatype", + "name": "jackson-datatype-jsr310", + "version": "2.11.2", + "description": "Add-on module to support JSR-310 (Java 8 Date & Time API) data types.", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "10fc883f659e2407ea2639fe6dd5b112" + }, + { + "alg": "SHA-1", + "content": "e6235e5eb3cf3edd2a95cd0dc96bc48aeb309e8a" + }, + { + "alg": "SHA-256", + "content": "c8f7155c405cf1c521fb7f1cde610a0c488aad794b3c4ca7637a199dbc40850f" + }, + { + "alg": "SHA-512", + "content": "c95f97b4b14e7d9f7d70c82257314f4aec46bd78f5d632ca2a7484920ff8be699f0b51cbb45e2a80aacd4abd28d874d02a1f667c598eb99d1469dffdbe68aa3a" + }, + { + "alg": "SHA-384", + "content": "0f391487c86e8bdb423bc90494885586fd3e9b497f7220bc170ad6d8cdec6a146f73140573f4af82e1ec9faa159f86b2" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/com.fasterxml.jackson.datatype/jackson-datatype-jsr310@2.11.2?type=jar", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.datatype/jackson-datatype-jsr310@2.11.2?type=jar" + }, + { + "author": "", + "publisher": "FasterXML", + "group": "com.fasterxml.jackson.module", + "name": "jackson-module-parameter-names", + "version": "2.11.2", + "description": "Add-on module for Jackson (http://jackson.codehaus.org) to support introspection of method/constructor parameter names, without having to add explicit property name annotation.", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "eed352dcdf5b22864084e25ed0671e84" + }, + { + "alg": "SHA-1", + "content": "c0dc526fcef5a3aae0273fc516ecf3505f7a5de8" + }, + { + "alg": "SHA-256", + "content": "1b5a7e61314a135bb44e99978801a111f18eb02c9bfe5cc5e5c9edb033369004" + }, + { + "alg": "SHA-512", + "content": "2fbe7ef3046df8a546ee178a3f68c779adfe2b08ad566937f67889b6262a753e5fe2503272e25de4c075e81438f261c3ca6f4e260b42468ac3d615a04b11e9c4" + }, + { + "alg": "SHA-384", + "content": "d67f7003653902daa15dbed3089c228894e92174d09434df0d9c8060bacc789cfaa5a70c171426bbede70e8de16f99b1" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/com.fasterxml.jackson.module/jackson-module-parameter-names@2.11.2?type=jar", + "type": "library", + "bom-ref": "pkg:maven/com.fasterxml.jackson.module/jackson-module-parameter-names@2.11.2?type=jar" + }, + { + "author": "", + "publisher": "Pivotal Software, Inc.", + "group": "org.springframework.boot", + "name": "spring-boot-starter-tomcat", + "version": "2.3.3.RELEASE", + "description": "Starter for using Tomcat as the embedded servlet container. Default servlet container starter used by spring-boot-starter-web", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "10b15934c487c45ea1235bbb9ab490e6" + }, + { + "alg": "SHA-1", + "content": "542a0aaf0f584d186d9bd052ab10a5cf357a5b39" + }, + { + "alg": "SHA-256", + "content": "8333f472a9a9e0e075e72ddb6084e0b32e2294b2fc386e50de0ecd1b68a561f2" + }, + { + "alg": "SHA-512", + "content": "b5cec59b0bc70969481d216bb44b535e3c6163d51d9255c2a4411f4eb832606df80b159b56b4045ce02bf4000b2b68cac32a5a2527a8cf35b578f1b877fd6741" + }, + { + "alg": "SHA-384", + "content": "90f60ce4d1ddcef99dfd9369c62320fdb4ba55cc1d0395bce35ee9c9816e5bc4abd45cc9363b88c827fa4bda57f44113" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework.boot/spring-boot-starter-tomcat@2.3.3.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework.boot/spring-boot-starter-tomcat@2.3.3.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "", + "group": "org.apache.tomcat.embed", + "name": "tomcat-embed-core", + "version": "9.0.37", + "description": "Core Tomcat implementation", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "cb726157d8e38b8dbc539393811cf8a3" + }, + { + "alg": "SHA-1", + "content": "c3f788de87f17eb57a9e7083736c1820fcbc1046" + }, + { + "alg": "SHA-256", + "content": "9713f639b5c06c4a1276f30dd9275df5ebc7bbb7a8581617d85b3f88da250096" + }, + { + "alg": "SHA-512", + "content": "be494336631ddb7fb4859de1bd7395409dfb21fa819a4a21c37e5acaea89bf1d742b7f64a808705f173838722238d63728e0d6f8f112556291a969d5d9bc2a1a" + }, + { + "alg": "SHA-384", + "content": "49b36db96bbe0cb040b30bcbfd984f3b93a73ad8d2bf1c064244569e9ca6b5101c69ea55e90ad1c1414c359aa9597f34" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@9.0.37?type=jar", + "type": "library", + "bom-ref": "pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@9.0.37?type=jar" + }, + { + "author": "", + "publisher": "Eclipse Foundation", + "group": "org.glassfish", + "name": "jakarta.el", + "version": "3.0.3", + "description": "Jakarta Expression Language provides a specification document, API, reference implementation and TCK that describes an expression language for Java applications.", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "6bb54dbf912bf1b6c79592838db76b51" + }, + { + "alg": "SHA-1", + "content": "dab46ee1ee23f7197c13d7c40fce14817c9017df" + }, + { + "alg": "SHA-256", + "content": "e2bcb8551b02a5c2afdc4cab77302ba5c76705cf1fc832345ca880df80bf4716" + }, + { + "alg": "SHA-512", + "content": "ec6bb8d89c59ea5e5b03c2d8a9cf3013900ecda84ccad89768820b04bedc2f6f837d36793e3a6cbeff4fa5adc167ce682c8ce643c7051f6fd64a5471a249fd22" + }, + { + "alg": "SHA-384", + "content": "b0b28cd09398a93b2bf63a66d43b6bebfca5ec557e8129f0c4e695d90d5e0b68c42abbe97fc11a85e976ecead2515393" + } + ], + "licenses": [ + { + "license": { + "id": "EPL-2.0" + } + }, + { + "license": { + "id": "GPL-2.0-with-classpath-exception" + } + } + ], + "purl": "pkg:maven/org.glassfish/jakarta.el@3.0.3?type=jar", + "type": "library", + "bom-ref": "pkg:maven/org.glassfish/jakarta.el@3.0.3?type=jar" + }, + { + "author": "", + "publisher": "", + "group": "org.apache.tomcat.embed", + "name": "tomcat-embed-websocket", + "version": "9.0.37", + "description": "Core Tomcat implementation", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "3ab6927df7e21c8e2b1e39a78adab5b5" + }, + { + "alg": "SHA-1", + "content": "ee8b7c9081372bf40c41443c93317145a01e343a" + }, + { + "alg": "SHA-256", + "content": "0939ca255d21fc0a40f42f11f7f169dd9a06e3ee1b0a227c0bc3395cd7a72c0e" + }, + { + "alg": "SHA-512", + "content": "4add1db9ebc2cefb331c46ecbdaf7e85b9d9e4ee4602018935da795401b6bc5266252e8b7a0f0d54df9845c8fae53f6fc5057a949d1432c6933ce85ac82b42c1" + }, + { + "alg": "SHA-384", + "content": "d72f4c07b12431796f0e4fedbd2026aa3ce87a875a0a23ed35b7565252135dba1f2a474f7e91f34247aa4d2fdfd91348" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.apache.tomcat.embed/tomcat-embed-websocket@9.0.37?type=jar", + "type": "library", + "bom-ref": "pkg:maven/org.apache.tomcat.embed/tomcat-embed-websocket@9.0.37?type=jar" + }, + { + "author": "", + "publisher": "Spring IO", + "group": "org.springframework", + "name": "spring-web", + "version": "5.2.8.RELEASE", + "description": "Spring Web", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "e97bd94371aa607529a0f65424e22f27" + }, + { + "alg": "SHA-1", + "content": "4f9542d61fff7beb6050e8028dfb6b7c6844c99a" + }, + { + "alg": "SHA-256", + "content": "5d1d897f7bd2eaa2cdc68fb23c845aa54a8bc88e88b150a29e5fc4b126790c79" + }, + { + "alg": "SHA-512", + "content": "f2849e7f322d7ee10478b7c69ab8373a93ae09966294a5dc4456af8ef1526c3d0a3207db86e62d76764b862f27b148e459c22ceb14d867499f27a4d5fd6ed92b" + }, + { + "alg": "SHA-384", + "content": "c88603f7840992cef2990f23d2420a905c151d8fffe3cabd1af048f045996bf2f26f3f3009142a63ce6e0531d3ca21eb" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework/spring-web@5.2.8.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework/spring-web@5.2.8.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Spring IO", + "group": "org.springframework", + "name": "spring-beans", + "version": "5.2.8.RELEASE", + "description": "Spring Beans", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "e1bdd09fa9edd2025cd2d325954487e9" + }, + { + "alg": "SHA-1", + "content": "5cfafc2b0f821bda0b537449a6fdf634b0a666ff" + }, + { + "alg": "SHA-256", + "content": "dc4e0f8014ca6c4ffe02e854e2f3fd548c384f208a69808f08d7ea56ab0849a0" + }, + { + "alg": "SHA-512", + "content": "5b54987a28b80f4b34495f792ee2ae2670062ff9f9ee199d3fab9e6f878be8057dfc65f0c08be44c8aa0c22c09614f61c018e239accd6ecf5bca5249d9b3f114" + }, + { + "alg": "SHA-384", + "content": "21ad646aadb88a3cbb7344665ed7f23b8aec8508193096be0d76465efb544788a57429725a784b11d2504be094ddab7b" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework/spring-beans@5.2.8.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework/spring-beans@5.2.8.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Spring IO", + "group": "org.springframework", + "name": "spring-webmvc", + "version": "5.2.8.RELEASE", + "description": "Spring Web MVC", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "33a0f883920679abd4f3e083f36f6016" + }, + { + "alg": "SHA-1", + "content": "9cccf8354a7e031e217681db33f14339200dffcf" + }, + { + "alg": "SHA-256", + "content": "4d1e85964f1ee339e9a9b0244163a2f2cc37f1eeb09317c9313e2678a13ac20c" + }, + { + "alg": "SHA-512", + "content": "83924fcf29080fb7cd4e2d4079b555457787d27c0f046ecb118686da956d4c7f02b79e2dccc16224ec707c33785eb51123c1b71337ece0b4e0553b3462dd614a" + }, + { + "alg": "SHA-384", + "content": "9e8e26c7bdd8339fc44622306020fdc8f11467acf7528718c4b6808510fe9009cf26fb7d60b486c8cfae8219a60f693b" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework/spring-webmvc@5.2.8.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework/spring-webmvc@5.2.8.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Spring IO", + "group": "org.springframework", + "name": "spring-aop", + "version": "5.2.8.RELEASE", + "description": "Spring AOP", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "4b3759cea5fa06f4e3ed6515b75b0e66" + }, + { + "alg": "SHA-1", + "content": "e0e9b4ed80ecde4bad258aaa3f87bf16eb1feecc" + }, + { + "alg": "SHA-256", + "content": "32b075c8ea140b4b6d0fe2dacbf014e3c9ab8076607f33cc27ab2fa264e16c04" + }, + { + "alg": "SHA-512", + "content": "b2ff2fc91957293464b62b121502762a421c44b7e673e23391a02b562b242ed6ad52c9a39c3b362a6a7e29b272a00a3c65a97ed65c0a0392746d21b3de69f27a" + }, + { + "alg": "SHA-384", + "content": "13519d4d8488aa4cc959044fe17a0a6388a0c35dd5c3a92fc0799fe8848630698c59f13e5dbc0ac7b73576f39c341d1f" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework/spring-aop@5.2.8.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework/spring-aop@5.2.8.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Spring IO", + "group": "org.springframework", + "name": "spring-context", + "version": "5.2.8.RELEASE", + "description": "Spring Context", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "d053d87f4509ba6eeeffe7ce10993333" + }, + { + "alg": "SHA-1", + "content": "304d59d6c9fda8bc651fecc8c49748f8259de5ce" + }, + { + "alg": "SHA-256", + "content": "0b82d773d268f094d886c0cafee7b9a15690c1e35d7ed089b5425547f0d5c191" + }, + { + "alg": "SHA-512", + "content": "a3fbe7d2c43da7106f7dcd5f44da35505ff775e749c84647f085c393a687151b80fa457bccd7366c990d0c1a41593c77d8c7953fcd9dd3c8dd9803f7a1fae1ca" + }, + { + "alg": "SHA-384", + "content": "caae534db73549d4cdfc2bd2c261fec7cc29db4eace4192641b968d74959b2926e023b5b35f190c5b83373c92558ab13" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework/spring-context@5.2.8.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework/spring-context@5.2.8.RELEASE?type=jar" + }, + { + "author": "", + "publisher": "Spring IO", + "group": "org.springframework", + "name": "spring-expression", + "version": "5.2.8.RELEASE", + "description": "Spring Expression Language (SpEL)", + "scope": "required", + "hashes": [ + { + "alg": "MD5", + "content": "ac6807dd5e4214d47e77301a74f973f4" + }, + { + "alg": "SHA-1", + "content": "1b3b5c84e83b450357ae84135e1b47d1b4e5a217" + }, + { + "alg": "SHA-256", + "content": "3121648f99bfad15640b6894a6517ff8176c32f7ceadbceefa3b2ee353426a4f" + }, + { + "alg": "SHA-512", + "content": "47a9c59c4b3a104768b5713d83054af2db5bce0531acfad56b1152a4ee02035a700317359747c7fd77c0adfc01c2ee09b4c3d15bb0a32ce53a25339c33181c0f" + }, + { + "alg": "SHA-384", + "content": "27dc0d76533f2cfa54f97c661612afe2faa6989fd36030eebc129e4094fe805ff4e809db1d2784571f38af58d944426b" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0" + } + } + ], + "purl": "pkg:maven/org.springframework/spring-expression@5.2.8.RELEASE?type=jar", + "type": "framework", + "bom-ref": "pkg:maven/org.springframework/spring-expression@5.2.8.RELEASE?type=jar" + } + ], + "dependencies": [ + { + "ref": "pkg:maven/some.name/hello-world@0.0.1-SNAPSHOT?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework.boot/spring-boot-starter-web@2.3.3.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework.boot/spring-boot-starter-web@2.3.3.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework.boot/spring-boot-starter@2.3.3.RELEASE?type=jar", + "pkg:maven/org.springframework.boot/spring-boot-starter-json@2.3.3.RELEASE?type=jar", + "pkg:maven/org.springframework.boot/spring-boot-starter-tomcat@2.3.3.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-web@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-webmvc@5.2.8.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework.boot/spring-boot-starter@2.3.3.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework.boot/spring-boot@2.3.3.RELEASE?type=jar", + "pkg:maven/org.springframework.boot/spring-boot-autoconfigure@2.3.3.RELEASE?type=jar", + "pkg:maven/org.springframework.boot/spring-boot-starter-logging@2.3.3.RELEASE?type=jar", + "pkg:maven/jakarta.annotation/jakarta.annotation-api@1.3.5?type=jar", + "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar", + "pkg:maven/org.yaml/snakeyaml@1.26?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework.boot/spring-boot@2.3.3.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-context@5.2.8.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework/spring-jcl@5.2.8.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework/spring-jcl@5.2.8.RELEASE?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.springframework/spring-context@5.2.8.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework/spring-aop@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-beans@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-expression@5.2.8.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework/spring-aop@5.2.8.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework/spring-beans@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework/spring-beans@5.2.8.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework/spring-expression@5.2.8.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework.boot/spring-boot-autoconfigure@2.3.3.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework.boot/spring-boot@2.3.3.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework.boot/spring-boot-starter-logging@2.3.3.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/ch.qos.logback/logback-classic@1.2.3?type=jar", + "pkg:maven/org.apache.logging.log4j/log4j-to-slf4j@2.13.3?type=jar", + "pkg:maven/org.slf4j/jul-to-slf4j@1.7.30?type=jar" + ] + }, + { + "ref": "pkg:maven/ch.qos.logback/logback-classic@1.2.3?type=jar", + "dependsOn": [ + "pkg:maven/ch.qos.logback/logback-core@1.2.3?type=jar", + "pkg:maven/org.slf4j/slf4j-api@1.7.30?type=jar" + ] + }, + { + "ref": "pkg:maven/ch.qos.logback/logback-core@1.2.3?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.slf4j/slf4j-api@1.7.30?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.apache.logging.log4j/log4j-to-slf4j@2.13.3?type=jar", + "dependsOn": [ + "pkg:maven/org.slf4j/slf4j-api@1.7.30?type=jar", + "pkg:maven/org.apache.logging.log4j/log4j-api@2.13.3?type=jar" + ] + }, + { + "ref": "pkg:maven/org.apache.logging.log4j/log4j-api@2.13.3?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.slf4j/jul-to-slf4j@1.7.30?type=jar", + "dependsOn": [ + "pkg:maven/org.slf4j/slf4j-api@1.7.30?type=jar" + ] + }, + { + "ref": "pkg:maven/jakarta.annotation/jakarta.annotation-api@1.3.5?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.yaml/snakeyaml@1.26?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.springframework.boot/spring-boot-starter-json@2.3.3.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework.boot/spring-boot-starter@2.3.3.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-web@5.2.8.RELEASE?type=jar", + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.11.2?type=jar", + "pkg:maven/com.fasterxml.jackson.datatype/jackson-datatype-jdk8@2.11.2?type=jar", + "pkg:maven/com.fasterxml.jackson.datatype/jackson-datatype-jsr310@2.11.2?type=jar", + "pkg:maven/com.fasterxml.jackson.module/jackson-module-parameter-names@2.11.2?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework/spring-web@5.2.8.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework/spring-beans@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar" + ] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.11.2?type=jar", + "dependsOn": [ + "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.11.2?type=jar", + "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.11.2?type=jar" + ] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.11.2?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.11.2?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.datatype/jackson-datatype-jdk8@2.11.2?type=jar", + "dependsOn": [ + "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.11.2?type=jar", + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.11.2?type=jar" + ] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.datatype/jackson-datatype-jsr310@2.11.2?type=jar", + "dependsOn": [ + "pkg:maven/com.fasterxml.jackson.core/jackson-annotations@2.11.2?type=jar", + "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.11.2?type=jar", + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.11.2?type=jar" + ] + }, + { + "ref": "pkg:maven/com.fasterxml.jackson.module/jackson-module-parameter-names@2.11.2?type=jar", + "dependsOn": [ + "pkg:maven/com.fasterxml.jackson.core/jackson-core@2.11.2?type=jar", + "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.11.2?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework.boot/spring-boot-starter-tomcat@2.3.3.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/jakarta.annotation/jakarta.annotation-api@1.3.5?type=jar", + "pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@9.0.37?type=jar", + "pkg:maven/org.glassfish/jakarta.el@3.0.3?type=jar", + "pkg:maven/org.apache.tomcat.embed/tomcat-embed-websocket@9.0.37?type=jar" + ] + }, + { + "ref": "pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@9.0.37?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.glassfish/jakarta.el@3.0.3?type=jar", + "dependsOn": [] + }, + { + "ref": "pkg:maven/org.apache.tomcat.embed/tomcat-embed-websocket@9.0.37?type=jar", + "dependsOn": [ + "pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@9.0.37?type=jar" + ] + }, + { + "ref": "pkg:maven/org.springframework/spring-webmvc@5.2.8.RELEASE?type=jar", + "dependsOn": [ + "pkg:maven/org.springframework/spring-aop@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-beans@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-context@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-core@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-expression@5.2.8.RELEASE?type=jar", + "pkg:maven/org.springframework/spring-web@5.2.8.RELEASE?type=jar" + ] + } + ], + "externalReferences": [ + { + "type": "other", + "url": ".", + "comment": "Base path" + }, + { + "type": "other", + "url": "C:/Users/dungpham/Desktop/spring-boot-hello-world/pom.xml", + "comment": "Package file" + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/npmsbom.json b/core/src/test/resources/npmsbom.json new file mode 100644 index 00000000..11a1b79f --- /dev/null +++ b/core/src/test/resources/npmsbom.json @@ -0,0 +1,3551 @@ +{ + "bomFormat": "CycloneDX", + "specVersion": "1.4", + "serialNumber": "urn:uuid:5bde5bcc-57f6-4802-b82b-d30565d5855f", + "version": 1, + "metadata": { + "timestamp": "2023-07-05T08:25:34.184Z", + "tools": [ + { + "vendor": "cyclonedx", + "name": "cdxgen", + "version": "8.6.0" + } + ], + "authors": [ + { + "name": "Prabhu Subramanian", + "email": "prabhu@appthreat.com" + } + ], + "component": { + "author": "", + "publisher": "", + "group": "", + "name": "sample-node-project", + "version": "1.0.0", + "description": "", + "licenses": [], + "purl": "pkg:application/sample-node-project@1.0.0", + "type": "application", + "bom-ref": "pkg:application/sample-node-project@1.0.0" + } + }, + "components": [ + { + "author": "", + "publisher": "", + "group": "", + "name": "accepts", + "version": "1.3.8", + "description": "Higher-level content negotiation", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "3d802d8536b69b654ac6ebd20f70cf0bf1b2f94fac380d4b02e4fc9a4991bafc3e34009269e5c443e34771517bace365eaa71ac55dd4b9e9b06b093eefe4892f" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/accepts@1.3.8", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/accepts#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/accepts.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/accepts@1.3.8", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "ansi-styles", + "version": "4.3.0", + "description": "ANSI escape codes for styling strings in the terminal", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "cdb07dac22404f5adb8e25436f686a2851cd60bc60b64f0d511c59dc86700f717a36dc5b5d94029e74a2d4b931f880e885d3e5169db6db05402c885e64941212" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/ansi-styles@4.3.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/chalk/ansi-styles#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/chalk/ansi-styles.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/ansi-styles@4.3.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "array-flatten", + "version": "1.1.1", + "description": "Flatten nested arrays", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "3c254042cc167a6bba51dc6c0c5157ffe815798a8a0287770f75159bdd631f0ca782e3b002f60f871f2736533ef8da9170ae82c71a5469f8e684874a88789baa" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/array-flatten@1.1.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/blakeembrey/array-flatten" + }, + { + "type": "vcs", + "url": "git://github.com/blakeembrey/array-flatten.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/array-flatten@1.1.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "async", + "version": "3.2.4", + "description": "Higher-order functions and common patterns for asynchronous code", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "88007e25b0c41978722146afa0397d58ffc98f5d3a2b3f43127d433e0630e6bb839f47b75a08b7b0a166e79b0049d18134e401f05e7d77da90edd7aaac703cc1" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/async@3.2.4", + "externalReferences": [ + { + "type": "website", + "url": "https://caolan.github.io/async/" + }, + { + "type": "vcs", + "url": "git+https://github.com/caolan/async.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/async@3.2.4", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "balanced-match", + "version": "1.0.2", + "description": "Match balanced character pairs, like \"{\" and \"}\"", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "de849e50ed13315ebb84dd4099b5ec2b8c9aa94eed8e21e56f144364ea47d0a5bdf82797e1b440697d009f1b74b71d8cae94695b041a3f02252121098585393f" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/balanced-match@1.0.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/juliangruber/balanced-match" + }, + { + "type": "vcs", + "url": "git://github.com/juliangruber/balanced-match.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/balanced-match@1.0.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "body-parser", + "version": "1.20.1", + "description": "Node.js body parsing middleware", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "8d68bb69b4db6306a33b2b56090737ed5ba599689169ee51c93a5a0b20dc4b9fe531db704b3e653a90c4ebbb2bc3f1d87b7e5fd73ddf0d0c3ededc60ee036d5b" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/body-parser@1.20.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/expressjs/body-parser#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/expressjs/body-parser.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/body-parser@1.20.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "brace-expansion", + "version": "1.1.11", + "description": "Brace expansion as known from sh/bash", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "882b8f1c3160ac75fb1f6bc423fe71a73d3bcd21c1d344e9ba0aa1998b5598c3bae75f260ae44ca0e60595d101974835f3bb9fa3375a1e058a71815beb5a8688" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/brace-expansion@1.1.11", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/juliangruber/brace-expansion" + }, + { + "type": "vcs", + "url": "git://github.com/juliangruber/brace-expansion.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/brace-expansion@1.1.11", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "bytes", + "version": "3.1.2", + "description": "Utility to parse a string bytes to bytes and vice-versa", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "fcd7fb4f2cd3c7a4b7c9124e6ce015efde7aafc72bdbe3a3f000b976df3048fdc1400a1e5f9f0da07c8253c3fccc690d5d2b634d28ba7f33ba174a4175c61b12" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/bytes@3.1.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/visionmedia/bytes.js#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/visionmedia/bytes.js.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/bytes@3.1.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "call-bind", + "version": "1.0.2", + "description": "Robustly `.call.bind()` a function", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "ecef856c28a1ac1e5619b1587ac72dc264ca69eeab3a22339b3d6272b79627ed1a03b2c97eeaa112ca364fd9dca5c16dccc42dcd77f64061ae7962464d8b2aac" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/call-bind@1.0.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/ljharb/call-bind#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/ljharb/call-bind.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/call-bind@1.0.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "chalk", + "version": "4.1.2", + "description": "Terminal string styling done right", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "a0a9db845c91217a54b9ecfc881326c846b89db8f820e432ba173fc32f6463bfd654f73020ef5503aebc3eef1190eefed06efa48b44e7b2c3d0a9434eb58b898" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/chalk@4.1.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/chalk/chalk#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/chalk/chalk.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/chalk@4.1.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "color-convert", + "version": "2.0.1", + "description": "Plain color conversion functions", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "4511023ec8fb8aeff16f9a0a61cb051d2a6914d9ec8ffe763954d129be333f9a275f0545df3566993a0d70e7c60be0910e97cafd4e7ce1f320dfc64709a12529" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/color-convert@2.0.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/Qix-/color-convert#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/Qix-/color-convert.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/color-convert@2.0.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "color-name", + "version": "1.1.4", + "description": "A list of color names and its values", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "74ecbedc0b96ddadb035b64722e319a537208c6b8b53fb812ffb9b71917d3976c3a3c7dfe0ef32569e417f479f4bcb84a18a39ab8171edd63d3a04065e002c40" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/color-name@1.1.4", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/colorjs/color-name" + }, + { + "type": "vcs", + "url": "git+ssh://git@github.com/colorjs/color-name.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/color-name@1.1.4", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "concat-map", + "version": "0.0.1", + "description": "concatenative mapdashery", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "fd2aefe1db30c903417e8846a73f68e986f71b3dd2ad40ea047e6b4ee84647b6a1b656d82a7571c366c214c4658da03b1171da5d9f30b07768745bdb9212a6aa" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/concat-map@0.0.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/ljharb/concat-map#readme" + }, + { + "type": "vcs", + "url": "git://github.com/ljharb/concat-map.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/concat-map@0.0.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "content-disposition", + "version": "0.5.4", + "description": "Create and parse Content-Disposition header", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "16f7994cdb86c34e1cc6502259bce2eb34c02ff9617a16966d3b6096e261e3f13de43a8cc139a16b7299375680580f1c148847ccc654bcb7af930e51aa4fad49" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/content-disposition@0.5.4", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/content-disposition#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/content-disposition.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/content-disposition@0.5.4", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "content-type", + "version": "1.0.5", + "description": "Create and parse HTTP Content-Type header", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "9d38ea7dc045122a4a7570afe180d05827e670b64a9bcd65745d29028a53bf2ac51956dc47a3ff54001de46ecdfb4b53afc42a894d2d15a743e852b836d27038" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/content-type@1.0.5", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/content-type#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/content-type.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/content-type@1.0.5", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "cookie", + "version": "0.5.0", + "description": "HTTP server cookie parsing and serialization", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "619dc65329ffa3c81f289967957ee0ef1ab88323ba392ba118f29a686b2c181daa803512d203e0b53be8c992d3b7d01be9d0b885f73d755e5aae4bdcfce0a6af" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/cookie@0.5.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/cookie#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/cookie.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/cookie@0.5.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "cookie-signature", + "version": "1.0.6", + "description": "Sign and unsign cookies", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "4000f395a1dcf22715f08eef6da257270a1df47598a7cb82a9fd716b839f36ed53ec9571408ad480e5ad1dd343b4f8b2c2615b892d76563a2d2172eb28cde8ad" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/cookie-signature@1.0.6", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/visionmedia/node-cookie-signature#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/visionmedia/node-cookie-signature.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/cookie-signature@1.0.6", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "debug", + "version": "2.6.9", + "description": "Lightweight debugging utility for Node.js and the browser", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "6c2ec496b7496899cf6c03fed44a2d62fa99b1bdde725e708ba05f8ba0494d470da30a7a72fb298348d7ce74532838e6fc4ec076014155e00f54c35c286b0730" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/debug@2.6.9", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/debug-js/debug#readme" + }, + { + "type": "vcs", + "url": "git://github.com/debug-js/debug.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/debug@2.6.9", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "depd", + "version": "2.0.0", + "description": "Deprecate all the things", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "83b9c7e8fe9dc838a8268800006a6b1a90ad5489898693e4feba02cdd6f77c887ad7fb3f9cfb1f47aa27c8cc2408047f3a50b7c810b49444af52840402cb08af" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/depd@2.0.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/dougwilson/nodejs-depd#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/dougwilson/nodejs-depd.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/depd@2.0.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "destroy", + "version": "1.2.0", + "description": "destroy a stream if possible", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "dac246253697208691d70e22252368374867318ec6a5cfe7f03e2a482270f10a855977fb72e0209c41f1069c1e69570f7af0b69772a98d80b1dcdca941081a26" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/destroy@1.2.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/stream-utils/destroy#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/stream-utils/destroy.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/destroy@1.2.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "ee-first", + "version": "1.1.1", + "description": "return the first event in a set of ee/event pairs", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "58cc26f4b851528f9651a44dfaf46e113a86f3d22066985548d91d16079beac4bf1383ab0c837bb78f0201ec121d773a0bc95e7c3f0a29faf9bd8eb56eb425a3" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/ee-first@1.1.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jonathanong/ee-first" + }, + { + "type": "vcs", + "url": "https://github.com/jonathanong/ee-first" + } + ], + "type": "library", + "bom-ref": "pkg:npm/ee-first@1.1.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "ejs", + "version": "3.1.9", + "description": "Embedded JavaScript templates", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "ac2f9054d3095aff8cb4f824b74cbed2b54421d6edc550030295bd257ad4565cc77ad81cc9cccffc4ec3266589e1f242645d9c3dc064d256963bd647306dc399" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0", + "url": "https://opensource.org/licenses/Apache-2.0" + } + } + ], + "purl": "pkg:npm/ejs@3.1.9", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/mde/ejs" + }, + { + "type": "vcs", + "url": "git://github.com/mde/ejs.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/ejs@3.1.9", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "encodeurl", + "version": "1.0.2", + "description": "Encode a URL to a percent-encoded form, excluding already-encoded sequences", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "4cf257abc26a15a5589b609698fbe73f6232a3865233bfd029c4a6b8c2c339b7e91f97e2ed150699dfeb4c37feaeeb7fb1a88389011e5533600262447403b1d3" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/encodeurl@1.0.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pillarjs/encodeurl#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/pillarjs/encodeurl.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/encodeurl@1.0.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "escape-html", + "version": "1.0.3", + "description": "Escape string for use in HTML", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "3624aea59e0e7ae1b0afaf251887b29bf92c219309a1d506392099fc54a74f172b7a46efaab81d53194938ca628da299563009ad6ac6b3fe89cbc38cbb28fda3" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/escape-html@1.0.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/component/escape-html" + }, + { + "type": "vcs", + "url": "https://github.com/component/escape-html" + } + ], + "type": "library", + "bom-ref": "pkg:npm/escape-html@1.0.3", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "etag", + "version": "1.8.1", + "description": "Create simple HTTP ETags", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "6882f9171ee66b055adf4d1a976067104e2236fa35a844f12eb3c8fe8d392fbcfa828edf0b0d49e844266cae05989d804bb920545fca1195ae7c17dd0a531c3e" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/etag@1.8.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/etag#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/etag.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/etag@1.8.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "express", + "version": "4.18.2", + "description": "Fast, unopinionated, minimalist web framework", + "scope": "required", + "hashes": [ + { + "alg": "SHA-512", + "content": "e7f3ec2fa8863dd7d0fe528cd54ba27a5620bf7054a097f3d5a53053dbc767e27b832bf07505c510120421ac5e19fd0621cade013372044c6d6a58ac0dbb8ca9" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/express@4.18.2", + "externalReferences": [ + { + "type": "website", + "url": "http://expressjs.com/" + }, + { + "type": "vcs", + "url": "git+https://github.com/expressjs/express.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/express@4.18.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "filelist", + "version": "1.0.4", + "description": "Lazy-evaluating list of files, based on globs or regex patterns", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "c35704b9fdd2f83acb0902fb113ea4cfe82694975babd27bc970928cafce6423c0faa10dd56c85e1901fd186096b8fec84726b6b6b7f77fafc495e098bec7ef1" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0", + "url": "https://opensource.org/licenses/Apache-2.0" + } + } + ], + "purl": "pkg:npm/filelist@1.0.4", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/mde/filelist" + }, + { + "type": "vcs", + "url": "git://github.com/mde/filelist.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/filelist@1.0.4", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "filelist/node_modules/brace-expansion", + "version": "2.0.1", + "description": "", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "5e7008bd0f1e33e902e9a50bc7ac2e422c15b27cec8bd7775b1cd5dc5a564c6035f45eb6d64c1d6ec01c14a5e02941d95accbe998ea22f5b074f1584142cad0c" + } + ], + "licenses": [], + "purl": "pkg:npm/filelist/node_modules/brace-expansion@2.0.1", + "type": "library", + "bom-ref": "pkg:npm/filelist/node_modules/brace-expansion@2.0.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "filelist/node_modules/minimatch", + "version": "5.1.6", + "description": "", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "94ac15ff56eba46ea6054147b5becd526b400426f65996669b6c0d88e0398406fc55d092e01dddb4c5b2bdca1589c730016fc23844635cbb74ccfd735d4376ea" + } + ], + "licenses": [], + "purl": "pkg:npm/filelist/node_modules/minimatch@5.1.6", + "type": "library", + "bom-ref": "pkg:npm/filelist/node_modules/minimatch@5.1.6", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "finalhandler", + "version": "1.2.0", + "description": "Node.js final http responder", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "e6e5dc5157ed9503059d60bdaaefecbe45afdc64ddd8f7d484aff73cb9183407bb15ba8932ddf9d791dac44e9e44bef819db2b8a2c2e8e26b075a0750691084a" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/finalhandler@1.2.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pillarjs/finalhandler#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/pillarjs/finalhandler.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/finalhandler@1.2.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "forwarded", + "version": "0.2.0", + "description": "Parse HTTP X-Forwarded-For header", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "6ee446d1fa41b511d24c238049eea10f6e7cb44b9b16844b6f864d03a3713151cdc3680e7301e8f70c9a6e5ccccce039cfdc40f4bd4a36393f36de8c4fd698a3" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/forwarded@0.2.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/forwarded#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/forwarded.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/forwarded@0.2.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "fresh", + "version": "0.5.2", + "description": "HTTP response freshness testing", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "cc9da6418335f2b1053ae75e57819285318843b45bcc0ee8cdb53d23f5c1a66ee4aa0332c209b294cc171f16499a45686249daf5dda95575573dd6133fd7a3f1" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/fresh@0.5.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/fresh#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/fresh.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/fresh@0.5.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "function-bind", + "version": "1.1.1", + "description": "Implementation of Function.prototype.bind", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "c88a2f033317e3db05f18979f1f482589e6cbd22ee6a26cfc5740914b98139b4ee0abd0c7f52a23e8a4633d3621638980426df69ad8587a6eb790e803554c8d0" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/function-bind@1.1.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/Raynos/function-bind" + }, + { + "type": "vcs", + "url": "git://github.com/Raynos/function-bind.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/function-bind@1.1.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "get-intrinsic", + "version": "1.2.1", + "description": "Get and robustly cache all JS language-level intrinsics at first require time", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "d8372cc9f00197e8151c47c239a4eb5a0cadfad6fa31210698aabe908e47c0b6c86208e098c715f0a438d6e68acf5c7151c9fdb49b6015b4141157046ddd0563" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/get-intrinsic@1.2.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/ljharb/get-intrinsic#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/ljharb/get-intrinsic.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/get-intrinsic@1.2.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "has", + "version": "1.0.3", + "description": "Object.prototype.hasOwnProperty.call shortcut", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "7f676f3b4554e8e7a3ed1916246ade8636f33008c5a79fd528fa79b53a56215e091c764ad7f0716c546d7ffb220364964ded3d71a0e656d618cd61086c14b8cf" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/has@1.0.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/tarruda/has" + }, + { + "type": "vcs", + "url": "git://github.com/tarruda/has.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/has@1.0.3", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "has-flag", + "version": "4.0.0", + "description": "Check if argv has a specific flag", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "1329094ff4352a34d672da698080207d23b4b4a56e6548e180caf5ee4a93ba6325e807efdc421295e53ba99533a170c54c01d30c2e0d3a81bf67153712f94c3d" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/has-flag@4.0.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/sindresorhus/has-flag#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/sindresorhus/has-flag.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/has-flag@4.0.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "has-proto", + "version": "1.0.1", + "description": "Does this environment have the ability to get the [[Prototype]] of an object on creation with `__proto__`?", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "eea13e88ff8ef9b805f5c944e7e528045cc4eb99a5062563ded282ae5350d0e8309b4063a53fe02b84a52d80ccc9b0e1e48dd30932a73cf6b4a0c1bb24362b86" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/has-proto@1.0.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/inspect-js/has-proto#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/inspect-js/has-proto.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/has-proto@1.0.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "has-symbols", + "version": "1.0.3", + "description": "Determine if the JS environment has Symbol support. Supports spec, or shams.", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "9772c2b85e8c8033704c32a47581848a1623b79a513db120e3aaed9669d23e551b82607c2ce22b2896d86050526e73da25ec4c2ad88f3bc8667918d1cf64ddf8" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/has-symbols@1.0.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/ljharb/has-symbols#readme" + }, + { + "type": "vcs", + "url": "git://github.com/inspect-js/has-symbols.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/has-symbols@1.0.3", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "http-errors", + "version": "2.0.0", + "description": "Create HTTP error objects", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "16dc2b1bf7ae0736848d8791a8e825cbb1b4aaf8a25e82569ef107d99d6994175781bca3bf7e291d349bf73a1e1ccc83cb7dfe0d6cb95adf56a3e4d446d39849" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/http-errors@2.0.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/http-errors#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/http-errors.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/http-errors@2.0.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "iconv-lite", + "version": "0.4.24", + "description": "Convert character encodings in pure javascript.", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "bf73179d901cbe7cb091350466898801cb657bb4575de79d391df5c3097b565ca85cee108bd6abbd27a73505a77b54dc4708422f51f02c8db56c4a9da63f3fac" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/iconv-lite@0.4.24", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/ashtuchkin/iconv-lite" + }, + { + "type": "vcs", + "url": "git://github.com/ashtuchkin/iconv-lite.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/iconv-lite@0.4.24", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "inherits", + "version": "2.0.4", + "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "93fbc6697e3f6256b75b3c8c0af4d039761e207bea38ab67a8176ecd31e9ce9419cc0b2428c859d8af849c189233dcc64a820578ca572b16b8758799210a9ec1" + } + ], + "licenses": [ + { + "license": { + "id": "ISC", + "url": "https://opensource.org/licenses/ISC" + } + } + ], + "purl": "pkg:npm/inherits@2.0.4", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/isaacs/inherits#readme" + }, + { + "type": "vcs", + "url": "git://github.com/isaacs/inherits.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/inherits@2.0.4", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "ipaddr.js", + "version": "1.9.1", + "description": "A library for manipulating IPv4 and IPv6 addresses in JavaScript.", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "d0a23feb4ef1a31493a07ec68cdd457d26cba14d3e6ed4e2723b1049642587f859ca437c2a998c7fbb98c0f5b747e6a467a47fc35f199574870585e26143cede" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/ipaddr.js@1.9.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/whitequark/ipaddr.js#readme" + }, + { + "type": "vcs", + "url": "git://github.com/whitequark/ipaddr.js.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/ipaddr.js@1.9.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "jake", + "version": "10.8.7", + "description": "JavaScript build tool, similar to Make or Rake", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "6438b768ff9f1bf2dc87207350cf34e158dd767c1f49fb1d798930b7c35c6ca46fa38ac592386ce39ea22c59f79366545af35ee22e3c5800836f36bc7e1ab6fb" + } + ], + "licenses": [ + { + "license": { + "id": "Apache-2.0", + "url": "https://opensource.org/licenses/Apache-2.0" + } + } + ], + "purl": "pkg:npm/jake@10.8.7", + "externalReferences": [ + { + "type": "vcs", + "url": "git://github.com/jakejs/jake.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/jake@10.8.7", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "media-typer", + "version": "0.3.0", + "description": "Simple RFC 6838 media type parser and formatter", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "76afaa7a543d6a41e970e97f8145514f15483a4009d70477400bdbe11b158d2f285681630c64dcebbf702589949a49d41791f030b3a06f93be6b72b17d66a93d" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/media-typer@0.3.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/media-typer#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/media-typer.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/media-typer@0.3.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "merge-descriptors", + "version": "1.0.1", + "description": "Merge objects using descriptors", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "7028ba837fd9af58aa422eb249bb1e3355efa286bdf0dd30df58f3518ad73d7db1a8e6e61461c9d2d439bbbe07de6561ef02e8b93b1e672608ab7f60f1c369d7" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/merge-descriptors@1.0.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/component/merge-descriptors" + }, + { + "type": "vcs", + "url": "https://github.com/component/merge-descriptors" + } + ], + "type": "library", + "bom-ref": "pkg:npm/merge-descriptors@1.0.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "methods", + "version": "1.1.2", + "description": "HTTP methods that node supports", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "89c9401de36a366ebccc5b676747bed4bdb250876fccda1ab8a53858103756f1ffbcf162785eea7d197051953e0c0f4ff5b3d7212f74ba5c68528087db7b15db" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/methods@1.1.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/methods" + }, + { + "type": "vcs", + "url": "https://github.com/jshttp/methods" + } + ], + "type": "library", + "bom-ref": "pkg:npm/methods@1.1.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "mime", + "version": "1.6.0", + "description": "A comprehensive library for mime-type mapping", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "c74567f2ca48fb0b89d4ee92ee09db69083c3f187834d1dbeca4883661162a23c4e1128ea65be28e7f8d92662699180febc99cef48f611b793151b2bb306907a" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/mime@1.6.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/broofa/mime#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/broofa/mime.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/mime@1.6.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "mime-db", + "version": "1.52.0", + "description": "Media Type Database", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "b0f538b95edd625bed589c70c311c3d0fba285536213b4f201b439496c43081f66518bce82ba103b061040e28f27c0886c4fb51135653a82b5502da7537818be" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/mime-db@1.52.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/mime-db#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/mime-db.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/mime-db@1.52.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "mime-types", + "version": "2.1.35", + "description": "The ultimate javascript content-type utility.", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "64363e6cf9b9cd34c5f98a42ac053d9cad148080983d3d10b53d4d65616fe2cfbe4cd91c815693d20ebee11dae238323423cf2b07075cf1b962f9d21cda7978b" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/mime-types@2.1.35", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/mime-types#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/mime-types.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/mime-types@2.1.35", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "minimatch", + "version": "3.1.2", + "description": "a glob matcher in javascript", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "27ba7ade1462023c35343130c355bb8b7efe07222b3963b95d0400cd9dd539c2f43cdc9bc297e657f374e73140cf043d512c84717eaddd43be2b96aa0503881f" + } + ], + "licenses": [ + { + "license": { + "id": "ISC", + "url": "https://opensource.org/licenses/ISC" + } + } + ], + "purl": "pkg:npm/minimatch@3.1.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/isaacs/minimatch#readme" + }, + { + "type": "vcs", + "url": "git://github.com/isaacs/minimatch.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/minimatch@3.1.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "ms", + "version": "2.0.0", + "description": "Tiny millisecond conversion utility", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "4e9a7ad0fe885090d3b8eabfe59f1c76c93326e8dfc2a7ce4e4af02308fb211212a679099d3e92c89e0f08f9c63281630bd75d85a979295218b40b7dee2c74e4" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/ms@2.0.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/vercel/ms#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/vercel/ms.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/ms@2.0.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "negotiator", + "version": "0.6.3", + "description": "HTTP content negotiation", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "f8452ca863cbb0cfa3ff37428598ec9d7e758385eb1c53885f07e70953c695093f9398226a470ab2ec4239b051bba0d29bda29c3f3bab2559b25d82140ce1b06" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/negotiator@0.6.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/negotiator#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/negotiator.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/negotiator@0.6.3", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "object-inspect", + "version": "1.12.3", + "description": "string representations of objects in node and the browser", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "81e52f764edcf9e8b330d503911a56d7027081f3a239e1dbc4147f84b5cad5a4face65523b48ec41cb3b7e3e8c1b0f3d8c2fdc8c67cb70d3abb4c62d1aa9bcd6" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/object-inspect@1.12.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/inspect-js/object-inspect" + }, + { + "type": "vcs", + "url": "git://github.com/inspect-js/object-inspect.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/object-inspect@1.12.3", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "on-finished", + "version": "2.4.1", + "description": "Execute a callback when a request closes, finishes, or errors", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "a15973920dc4340842936cddbfb209c1dfd0503e33d91c51c2991c198f29b0255c09864dab8c189d55802c733e6ebb6e26378f5a2605fc2966b83afc0a1e7e92" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/on-finished@2.4.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/on-finished#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/on-finished.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/on-finished@2.4.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "parseurl", + "version": "1.3.3", + "description": "parse a url with memoization", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "0a2c9e3b1153fc96723799b4cfd3df5f0e1208127a4b2833d43a65d30aa39610c418604fd469ec51510bd29eb78681b57dc8f77c7ca75e2f4d60ee2758e2fea9" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/parseurl@1.3.3", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pillarjs/parseurl#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/pillarjs/parseurl.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/parseurl@1.3.3", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "path-to-regexp", + "version": "0.1.7", + "description": "Express style path to RegExp utility", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "e43164ba8aa5bf5b9840ac72f2898505e24f41c768134ecabf6b1f7ab0c2ac0ab5a21394f8c483b300c86e7c7760033ad2a20e9d86b9df00615d6d046cca27ad" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/path-to-regexp@0.1.7", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pillarjs/path-to-regexp#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/pillarjs/path-to-regexp.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/path-to-regexp@0.1.7", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "proxy-addr", + "version": "2.0.7", + "description": "Determine address of proxied request", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "96542c30b4940d43d3e388ddad4fcedfbaa59e27e2b433fe670ae699972848ac8b2afb59c69c95d27dbf6c3fcde2d040019fe024475953b28cadaa0ad7e5d802" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/proxy-addr@2.0.7", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/proxy-addr#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/proxy-addr.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/proxy-addr@2.0.7", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "qs", + "version": "6.11.0", + "description": "A querystring parser that supports nesting and arrays, with a depth limit", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "32f8e830227011aad26d4624e4efa79a84b34aeb52b13c05f39cdc1cf43d3ab945a193982236aa040248a885e3a6dc83e6f4e1c46ab9d97bbf31a273464224e1" + } + ], + "licenses": [ + { + "license": { + "id": "BSD-3-Clause", + "url": "https://opensource.org/licenses/BSD-3-Clause" + } + } + ], + "purl": "pkg:npm/qs@6.11.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/ljharb/qs" + }, + { + "type": "vcs", + "url": "git+https://github.com/ljharb/qs.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/qs@6.11.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "range-parser", + "version": "1.2.1", + "description": "Range header field string parser", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "1eb82cc7ea2baa8ca09e68456ca68713a736f7a27e1d30105e8c4417a80dba944e9a6189468cb37c6ddc700bdea8206bc2bff6cb143905577f1939796a03b04a" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/range-parser@1.2.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/range-parser#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/range-parser.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/range-parser@1.2.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "raw-body", + "version": "2.5.1", + "description": "Get and validate the raw body of a readable stream.", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "aaa241b44c95812d1998f19d0853d627716b7a8aaf1b83154259ff902805ece96af7921b3a9d3f056c8cc1b76d9f8553be433c63b921090d97824fed72b0978a" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/raw-body@2.5.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/stream-utils/raw-body#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/stream-utils/raw-body.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/raw-body@2.5.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "safe-buffer", + "version": "5.2.1", + "description": "Safer Node.js Buffer API", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "ae9dd2a34eca71d9a629b1af81a37141226bedb1954959394bd12ad45fa9a5b468ef4f9879a0f1930e4377c34f37e183e9b8e7626d95b8fb825e6a6e62f9825d" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/safe-buffer@5.2.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/feross/safe-buffer" + }, + { + "type": "vcs", + "url": "git://github.com/feross/safe-buffer.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/safe-buffer@5.2.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "safer-buffer", + "version": "2.1.2", + "description": "Modern Buffer API polyfill without footguns", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "619a372bcd920fb462ca2d04d4440fa232f3ee4a5ea6749023d2323db1c78355d75debdbe5d248eeda72376003c467106c71bbbdcc911e4d1c6f0a9c42b894b6" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/safer-buffer@2.1.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/ChALkeR/safer-buffer#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/ChALkeR/safer-buffer.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/safer-buffer@2.1.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "send", + "version": "0.18.0", + "description": "Better streaming static file server with Range and conditional-GET support", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "aaa5b3b8e8d214ebaa3e315ee0d3ac30b69f4e8410c0148e1294be17012ddc0d95def2ae6d3aae4f7be62d3429160317a7c02515616e3f5a8a68964eb4fa555e" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/send@0.18.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/pillarjs/send#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/pillarjs/send.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/send@0.18.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "send/node_modules/ms", + "version": "2.1.3", + "description": "", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "e85973b9b4cb646dc9d9afcd542025784863ceae68c601f268253dc985ef70bb2fa1568726afece715c8ebf5d73fab73ed1f7100eb479d23bfb57b45dd645394" + } + ], + "licenses": [], + "purl": "pkg:npm/send/node_modules/ms@2.1.3", + "type": "library", + "bom-ref": "pkg:npm/send/node_modules/ms@2.1.3", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "serve-static", + "version": "1.15.0", + "description": "Serve static files", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "5c6b910cd8d75228ec50bd2f97a9d20fb730511bb31208256ce685b9933d8379300d7396553724d232f38cfcc60fe4dacd66dba1962ee76ffdfd73dd5209def6" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/serve-static@1.15.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/expressjs/serve-static#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/expressjs/serve-static.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/serve-static@1.15.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "setprototypeof", + "version": "1.2.0", + "description": "A small polyfill for Object.setprototypeof", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "1392c35fb5aba7ce4a8a5e5b859bf8ea3f2339e6e82aae4932660cde05467461fcc45a4f59750cb0dae53830ab928c4c11e362fd7648c2e46f6385cdc18309a7" + } + ], + "licenses": [ + { + "license": { + "id": "ISC", + "url": "https://opensource.org/licenses/ISC" + } + } + ], + "purl": "pkg:npm/setprototypeof@1.2.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/wesleytodd/setprototypeof" + }, + { + "type": "vcs", + "url": "git+https://github.com/wesleytodd/setprototypeof.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/setprototypeof@1.2.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "side-channel", + "version": "1.0.4", + "description": "Store information about any JS value in a side channel. Uses WeakMap if available.", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "ab95cfcada85108287906762308ad8d749af2d1be7421e36ffe1a8065156ddbd8b5cb136c71269645766f78c1ed016a85774702721aa839c12edea714efd19bf" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/side-channel@1.0.4", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/ljharb/side-channel#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/ljharb/side-channel.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/side-channel@1.0.4", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "statuses", + "version": "2.0.1", + "description": "HTTP status utility", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "470340f59ffb3eb2b4eab60b23314c95a17e97bde2c29ceca9120581b30b6d370b0fa70e6a8f364da59e7cf5d0bc1d9f382e008ee612127752ecdfe64c26e475" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/statuses@2.0.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/statuses#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/statuses.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/statuses@2.0.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "supports-color", + "version": "7.2.0", + "description": "Detect whether a terminal supports color", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "aa9080bd197db2db8e1ef78ab27ec79dc251befe74d6a21a70acd094effe2f0c5cf7ed2adb02f2bf80dfbedf34fc33e7da9a8e06c25d0e2a205c647df8ebf047" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/supports-color@7.2.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/chalk/supports-color#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/chalk/supports-color.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/supports-color@7.2.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "toidentifier", + "version": "1.0.1", + "description": "Convert a string of words to a JavaScript identifier", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "a39b123ca12483f0c840d987e37574fee7ab2eba7355e764521f2d18dbda797a5fa6ec2329e9e54a8c7fd8efc14e5654b447be246eece58844cfad3c3e500744" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/toidentifier@1.0.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/component/toidentifier#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/component/toidentifier.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/toidentifier@1.0.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "type-is", + "version": "1.6.18", + "description": "Infer the content-type of a request.", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "4e444aafdb144f1107f0c75fb8248fed58b3272cd134c8e3d89d9da3626bdcaca6e7df0955d124b2eccf4029e514f5b8932f50fa203e99af411a6d3a5d0072f2" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/type-is@1.6.18", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/type-is#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/type-is.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/type-is@1.6.18", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "unpipe", + "version": "1.0.0", + "description": "Unpipe a stream from all destinations", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "a63cb66d8852b2e7f05a52b03dcfa5ddc37bfb0b8994aeaecf461d2443a54036e5ea3a3f6253e2e266fc6a0524542f0117b57c36ecdec8f36a464b00de1ced29" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/unpipe@1.0.0", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/stream-utils/unpipe" + }, + { + "type": "vcs", + "url": "https://github.com/stream-utils/unpipe" + } + ], + "type": "library", + "bom-ref": "pkg:npm/unpipe@1.0.0", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "utils-merge", + "version": "1.0.1", + "description": "merge() utility function", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "a4c653bc8913d5df93146bc33aaa1d39c971d105a49208ba4dda1af200bc7df18002acfda733d36560326dbb071e8103ff3b4cb64bff5686136324a1527f3584" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/utils-merge@1.0.1", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jaredhanson/utils-merge#readme" + }, + { + "type": "vcs", + "url": "git://github.com/jaredhanson/utils-merge.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/utils-merge@1.0.1", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + }, + { + "author": "", + "publisher": "", + "group": "", + "name": "vary", + "version": "1.1.2", + "description": "Manipulate the HTTP Vary header", + "scope": "optional", + "hashes": [ + { + "alg": "SHA-512", + "content": "04d19b58b7ddd1e50f69b8645d4566d23f2ebaf444c93879a2f45afddca8c3f06a01b649c82fb97d4f88cd03b39802b362a6110084a8461750af778867f3d7aa" + } + ], + "licenses": [ + { + "license": { + "id": "MIT", + "url": "https://opensource.org/licenses/MIT" + } + } + ], + "purl": "pkg:npm/vary@1.1.2", + "externalReferences": [ + { + "type": "website", + "url": "https://github.com/jshttp/vary#readme" + }, + { + "type": "vcs", + "url": "git+https://github.com/jshttp/vary.git" + } + ], + "type": "library", + "bom-ref": "pkg:npm/vary@1.1.2", + "properties": [ + { + "name": "SrcFile", + "value": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json" + } + ] + } + ], + "dependencies": [ + { + "ref": "pkg:application/sample-node-project@1.0.0", + "dependsOn": [ + "pkg:npm/ejs@3.1.9", + "pkg:npm/express@4.18.2" + ] + }, + { + "ref": "pkg:npm/accepts@1.3.8", + "dependsOn": [ + "pkg:npm/mime-types", + "pkg:npm/negotiator" + ] + }, + { + "ref": "pkg:npm/ansi-styles@4.3.0", + "dependsOn": [ + "pkg:npm/color-convert" + ] + }, + { + "ref": "pkg:npm/array-flatten@1.1.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/async@3.2.4", + "dependsOn": [] + }, + { + "ref": "pkg:npm/balanced-match@1.0.2", + "dependsOn": [] + }, + { + "ref": "pkg:npm/body-parser@1.20.1", + "dependsOn": [ + "pkg:npm/bytes", + "pkg:npm/content-type", + "pkg:npm/debug", + "pkg:npm/depd", + "pkg:npm/destroy", + "pkg:npm/http-errors", + "pkg:npm/iconv-lite", + "pkg:npm/on-finished", + "pkg:npm/qs", + "pkg:npm/raw-body", + "pkg:npm/type-is", + "pkg:npm/unpipe" + ] + }, + { + "ref": "pkg:npm/brace-expansion@1.1.11", + "dependsOn": [ + "pkg:npm/balanced-match", + "pkg:npm/concat-map" + ] + }, + { + "ref": "pkg:npm/bytes@3.1.2", + "dependsOn": [] + }, + { + "ref": "pkg:npm/call-bind@1.0.2", + "dependsOn": [ + "pkg:npm/function-bind", + "pkg:npm/get-intrinsic" + ] + }, + { + "ref": "pkg:npm/chalk@4.1.2", + "dependsOn": [ + "pkg:npm/ansi-styles", + "pkg:npm/supports-color" + ] + }, + { + "ref": "pkg:npm/color-convert@2.0.1", + "dependsOn": [ + "pkg:npm/color-name" + ] + }, + { + "ref": "pkg:npm/color-name@1.1.4", + "dependsOn": [] + }, + { + "ref": "pkg:npm/concat-map@0.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/content-disposition@0.5.4", + "dependsOn": [ + "pkg:npm/safe-buffer" + ] + }, + { + "ref": "pkg:npm/content-type@1.0.5", + "dependsOn": [] + }, + { + "ref": "pkg:npm/cookie@0.5.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/cookie-signature@1.0.6", + "dependsOn": [] + }, + { + "ref": "pkg:npm/debug@2.6.9", + "dependsOn": [ + "pkg:npm/ms" + ] + }, + { + "ref": "pkg:npm/depd@2.0.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/destroy@1.2.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/ee-first@1.1.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/ejs@3.1.9", + "dependsOn": [ + "pkg:npm/jake" + ] + }, + { + "ref": "pkg:npm/encodeurl@1.0.2", + "dependsOn": [] + }, + { + "ref": "pkg:npm/escape-html@1.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:npm/etag@1.8.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/express@4.18.2", + "dependsOn": [ + "pkg:npm/accepts", + "pkg:npm/array-flatten", + "pkg:npm/body-parser", + "pkg:npm/content-disposition", + "pkg:npm/content-type", + "pkg:npm/cookie", + "pkg:npm/cookie-signature", + "pkg:npm/debug", + "pkg:npm/depd", + "pkg:npm/encodeurl", + "pkg:npm/escape-html", + "pkg:npm/etag", + "pkg:npm/finalhandler", + "pkg:npm/fresh", + "pkg:npm/http-errors", + "pkg:npm/merge-descriptors", + "pkg:npm/methods", + "pkg:npm/on-finished", + "pkg:npm/parseurl", + "pkg:npm/path-to-regexp", + "pkg:npm/proxy-addr", + "pkg:npm/qs", + "pkg:npm/range-parser", + "pkg:npm/safe-buffer", + "pkg:npm/send", + "pkg:npm/serve-static", + "pkg:npm/setprototypeof", + "pkg:npm/statuses", + "pkg:npm/type-is", + "pkg:npm/utils-merge", + "pkg:npm/vary" + ] + }, + { + "ref": "pkg:npm/filelist@1.0.4", + "dependsOn": [ + "pkg:npm/minimatch" + ] + }, + { + "ref": "pkg:npm/filelist/node_modules/brace-expansion@2.0.1", + "dependsOn": [ + "pkg:npm/balanced-match" + ] + }, + { + "ref": "pkg:npm/filelist/node_modules/minimatch@5.1.6", + "dependsOn": [ + "pkg:npm/brace-expansion" + ] + }, + { + "ref": "pkg:npm/finalhandler@1.2.0", + "dependsOn": [ + "pkg:npm/debug", + "pkg:npm/encodeurl", + "pkg:npm/escape-html", + "pkg:npm/on-finished", + "pkg:npm/parseurl", + "pkg:npm/statuses", + "pkg:npm/unpipe" + ] + }, + { + "ref": "pkg:npm/forwarded@0.2.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/fresh@0.5.2", + "dependsOn": [] + }, + { + "ref": "pkg:npm/function-bind@1.1.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/get-intrinsic@1.2.1", + "dependsOn": [ + "pkg:npm/function-bind", + "pkg:npm/has", + "pkg:npm/has-proto", + "pkg:npm/has-symbols" + ] + }, + { + "ref": "pkg:npm/has@1.0.3", + "dependsOn": [ + "pkg:npm/function-bind" + ] + }, + { + "ref": "pkg:npm/has-flag@4.0.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/has-proto@1.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/has-symbols@1.0.3", + "dependsOn": [] + }, + { + "ref": "pkg:npm/http-errors@2.0.0", + "dependsOn": [ + "pkg:npm/depd", + "pkg:npm/inherits", + "pkg:npm/setprototypeof", + "pkg:npm/statuses", + "pkg:npm/toidentifier" + ] + }, + { + "ref": "pkg:npm/iconv-lite@0.4.24", + "dependsOn": [ + "pkg:npm/safer-buffer" + ] + }, + { + "ref": "pkg:npm/inherits@2.0.4", + "dependsOn": [] + }, + { + "ref": "pkg:npm/ipaddr.js@1.9.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/jake@10.8.7", + "dependsOn": [ + "pkg:npm/async", + "pkg:npm/chalk", + "pkg:npm/filelist", + "pkg:npm/minimatch" + ] + }, + { + "ref": "pkg:npm/media-typer@0.3.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/merge-descriptors@1.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/methods@1.1.2", + "dependsOn": [] + }, + { + "ref": "pkg:npm/mime@1.6.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/mime-db@1.52.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/mime-types@2.1.35", + "dependsOn": [ + "pkg:npm/mime-db" + ] + }, + { + "ref": "pkg:npm/minimatch@3.1.2", + "dependsOn": [ + "pkg:npm/brace-expansion" + ] + }, + { + "ref": "pkg:npm/ms@2.0.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/negotiator@0.6.3", + "dependsOn": [] + }, + { + "ref": "pkg:npm/object-inspect@1.12.3", + "dependsOn": [] + }, + { + "ref": "pkg:npm/on-finished@2.4.1", + "dependsOn": [ + "pkg:npm/ee-first" + ] + }, + { + "ref": "pkg:npm/parseurl@1.3.3", + "dependsOn": [] + }, + { + "ref": "pkg:npm/path-to-regexp@0.1.7", + "dependsOn": [] + }, + { + "ref": "pkg:npm/proxy-addr@2.0.7", + "dependsOn": [ + "pkg:npm/forwarded", + "pkg:npm/ipaddr.js" + ] + }, + { + "ref": "pkg:npm/qs@6.11.0", + "dependsOn": [ + "pkg:npm/side-channel" + ] + }, + { + "ref": "pkg:npm/range-parser@1.2.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/raw-body@2.5.1", + "dependsOn": [ + "pkg:npm/bytes", + "pkg:npm/http-errors", + "pkg:npm/iconv-lite", + "pkg:npm/unpipe" + ] + }, + { + "ref": "pkg:npm/safe-buffer@5.2.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/safer-buffer@2.1.2", + "dependsOn": [] + }, + { + "ref": "pkg:npm/send@0.18.0", + "dependsOn": [ + "pkg:npm/debug", + "pkg:npm/depd", + "pkg:npm/destroy", + "pkg:npm/encodeurl", + "pkg:npm/escape-html", + "pkg:npm/etag", + "pkg:npm/fresh", + "pkg:npm/http-errors", + "pkg:npm/mime", + "pkg:npm/ms", + "pkg:npm/on-finished", + "pkg:npm/range-parser", + "pkg:npm/statuses" + ] + }, + { + "ref": "pkg:npm/send/node_modules/ms@2.1.3", + "dependsOn": [] + }, + { + "ref": "pkg:npm/serve-static@1.15.0", + "dependsOn": [ + "pkg:npm/encodeurl", + "pkg:npm/escape-html", + "pkg:npm/parseurl", + "pkg:npm/send" + ] + }, + { + "ref": "pkg:npm/setprototypeof@1.2.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/side-channel@1.0.4", + "dependsOn": [ + "pkg:npm/call-bind", + "pkg:npm/get-intrinsic", + "pkg:npm/object-inspect" + ] + }, + { + "ref": "pkg:npm/statuses@2.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/supports-color@7.2.0", + "dependsOn": [ + "pkg:npm/has-flag" + ] + }, + { + "ref": "pkg:npm/toidentifier@1.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/type-is@1.6.18", + "dependsOn": [ + "pkg:npm/media-typer", + "pkg:npm/mime-types" + ] + }, + { + "ref": "pkg:npm/unpipe@1.0.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/utils-merge@1.0.1", + "dependsOn": [] + }, + { + "ref": "pkg:npm/vary@1.1.2", + "dependsOn": [] + } + ], + "externalReferences": [ + { + "type": "other", + "url": ".", + "comment": "Base path" + }, + { + "type": "other", + "url": "C:/Users/dungpham/Desktop/npm-project-nodemodules/package-lock.json", + "comment": "Package file" + } + ] +} \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 666640c6..8ac4020f 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -890,6 +890,42 @@ In Solicitor the Data is read with the following part of the config NOTE: The former reader of type `gradle` is deprecated and should no longer be used. See <>. +=== CycloneDX + +The CycloneDX reader can read SBOMs in CycloneDX 1.4 or 1.5 format (https://cyclonedx.org/specification/overview/). +CDXGEN (https://github.com/CycloneDX/cdxgen) is one tool which can create an SBOM in the required format. + +To install CDXGEN, the following command needs to be executed. +---- +sudo npm install -g @cyclonedx/cdxgen +---- +To run CDXGEN, change into the project directory containing the build file (i.e. pom.xml, package.json). For npm projects, execute "npm-install" before running CDXGEN to create a package-lock.json. + +Set the FETCH_LICENSE environmental variable, to fetch the declared licenses. +---- +export FETCH_LICENSE=true +---- +Then execute the following command: +---- +cdxgen -o sbom.json +---- +The export should look like the following +---- +link:files/sbom.json[] +---- +In Solicitor, the data is read with the following part of the config +---- +"readers" : [ { + "type" : "cdxgen", + "source" : "file:$/input/sbom.json", + "usagePattern" : "DYNAMIC_LINKING" + "repoType" : "maven" //or any other valid repoType + } ] +---- + +NOTE: Currently, Solicitor only has packageUrlHandlers for maven, npm and pip. For all other package types, Solicitor will ignore the packageUrl. + +NOTE: In the "repoType" field, only ONE type is allowed to be set. In case the SBOM contains packages of multiple types, create a separate SBOM and reader configuration for each type. == Working with Decision Tables @@ -1355,17 +1391,17 @@ Results are stored in subdirectory `Source` of the directory `output` and is org ===== Origin file The Scancode integration scripts try to download ApplicationComponent sources from default URLs derived from the PackageUrl (e.g. Maven Central). In cases where the sources are not available at these locations, the download will fail (and the subsequent source scan will be skipped). In this case it is possible to manually download the sources from some other location and store it in the directory structure. Restarting the Scancode integration script might then perform the source scan. -To be able to document the (non default) origin of the ApplicationComponent sources a file `origin.yaml` is created in the components directory in the file system. If the failed source download has been performed manually it is possible to edit this file and correct the data given in this file. +To be able to document the (non default) origin of the ApplicationComponent sources a file `origin.yaml` is created in the components directory in the file system. If the failed source download has been performed manually it is possible to edit this file and correct the data given in this file. [source,yaml] ---- # This file contains metadata about the orgin of the package and the sources. # This file was automatically created but might manually be edited if the contained data is not correct sourceDownloadUrl: https://url/pointing/to/the/source/archive.jar <1> -packageDownloadUrl: https://url/pointing/to/the/binary/archive.jar <2> +packageDownloadUrl: https://url/pointing/to/the/binary/archive.jar <2> # note: to add comments: write them here and remove the hash at the beginning of the line (not yet processed by Solicitor) ---- -<1> URL for downloading the sources - will be available as property `ApplicationComponent.sourceDownloadUrl` in the Solicitor data model. +<1> URL for downloading the sources - will be available as property `ApplicationComponent.sourceDownloadUrl` in the Solicitor data model. <2> URL for downloading the binaries - will be available as property `ApplicationComponent.packageDownloadUrl` in the Solicitor data model. The content of the file `origin.yaml` currently just affects the above given two properties, it does not affect the downloading of sources by the scripts. @@ -1379,8 +1415,8 @@ _Solicitor_ will try to look up ScanCode information from the directory tree in * License information (including URL of license text) as obtained from the Readers is replaced by the license info found by ScanCode * Copyrights are taken from ScanCode results * Info on NOTICE file is taken from the ScanCode results -* If the ScanCode results contain information about project URLs this is stored as `sourceRepoUrl` and/or `ossHomepage` -* `sourceDownloadUrl` and `packageDownloadUrl` are set to the values given in file `origin.yaml` +* If the ScanCode results contain information about project URLs this is stored as `sourceRepoUrl` and/or `ossHomepage` +* `sourceDownloadUrl` and `packageDownloadUrl` are set to the values given in file `origin.yaml` ==== Output Main target of the additional information obtained from ScanCode is currently the new report `Attributions_PROJECTNAME.html` which lists @@ -1582,13 +1618,14 @@ Changes in 1.13.0:: Changes in 1.12.0:: * https://github.com/devonfw/solicitor/issues/182: Add new data quality and traceability attributes to the ComponentInfo data structure. * https://github.com/devonfw/solicitor/issues/185: Fixed random ordering of rows in OSS-Inventory-Simple, which causes false differences to be marked. +* https://github.com/devonfw/solicitor/issues/189: Added a CycloneDX Reader, to enable reading SBOMs in CycloneDX 1.4 or 1.5 format. Changes in 1.11.0:: * https://github.com/devonfw/solicitor/pull/179: Added further name mapping rules. * https://github.com/devonfw/solicitor/issues/168: Fixed NullPointerException for blank license in License URL Guessing. Changes in 1.10.0:: -* https://github.com/devonfw/solicitor/issues/175: Introduce new properties `packageDownloadUrl` and `sourceDownloadUrl` in `ApplicationComponent`. Process file `origin.yaml` within <> to be able to set these properties to non default values. +* https://github.com/devonfw/solicitor/issues/175: Introduce new properties `packageDownloadUrl` and `sourceDownloadUrl` in `ApplicationComponent`. Process file `origin.yaml` within <> to be able to set these properties to non default values. * https://github.com/devonfw/solicitor/issues/177: Contents of local resources (`file:`) are no longer cached in FilesystemCachingContentProvider. Changes in 1.9.0:: diff --git a/solicitor.dict b/solicitor.dict index a59ea484..f7fd7832 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -1,7 +1,9 @@ auth Capsulate +CDXGEN cfgdir conf +CycloneDX deliverables Dloader Dpropertyname @@ -23,6 +25,7 @@ nd ort oss RHS +SBOMs scancode SOLI sql From a5e531eaef0711c1270ace3796bd926b13c84815 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 8 Aug 2023 16:10:27 +0200 Subject: [PATCH 059/139] Fixed wrong type parameter of CycloneDX-Reader --- documentation/master-solicitor.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 8ac4020f..2fb929ea 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -916,7 +916,7 @@ link:files/sbom.json[] In Solicitor, the data is read with the following part of the config ---- "readers" : [ { - "type" : "cdxgen", + "type" : "cyclonedx", "source" : "file:$/input/sbom.json", "usagePattern" : "DYNAMIC_LINKING" "repoType" : "maven" //or any other valid repoType From 4a665525ca58b6a153ad1e6c8b69bf11d1f07e72 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:25:14 +0200 Subject: [PATCH 060/139] Refactor scancode file adapter (#193) --- .../ComponentContentProvider.java | 23 + .../componentinfo/ComponentInfo.java | 13 +- .../componentinfo/ComponentInfoAdapter.java | 20 +- .../ComponentInfoInventoryProcessor.java | 12 +- .../componentinfo/ComponentInfoProvider.java | 21 + .../DefaultComponentInfoImpl.java | 259 ++++++++++ .../componentinfo/DefaultLicenseInfoImpl.java | 77 +++ .../solicitor/componentinfo/LicenseInfo.java | 4 +- .../curation/ComponentInfoCurator.java | 21 + .../curation/ComponentInfoCuratorImpl.java | 96 ++++ .../CuratingComponentInfoAdapter.java | 71 +++ .../curation/CurationProvider.java | 22 + .../curation/SingleFileCurationProvider.java | 113 +++++ .../UncuratedComponentInfoProvider.java | 12 + .../curation/model/ComponentInfoCuration.java | 111 +++++ .../curation/model/CurationList.java | 37 ++ .../curation/model/LicenseInfoCuration.java | 54 +++ .../curation/model/package-info.java | 4 + .../componentinfo/curation/package-info.java | 8 + .../componentinfo_classes.drawio.svg | 3 + .../solicitor/componentinfo/package-info.java | 14 + .../FileScancodeRawComponentInfoProvider.java | 177 +++++++ .../scancode/ScancodeComponentInfo.java | 66 ++- .../ScancodeComponentInfoAdapter.java | 67 +++ .../scancode/ScancodeFileAdapter.java | 442 ------------------ .../scancode/ScancodeRawComponentInfo.java | 22 + .../ScancodeRawComponentInfoPovider.java | 44 ++ ...ncuratedScancodeComponentInfoProvider.java | 268 +++++++++++ .../componentinfo/scancode/package-info.java | 8 + .../devonfw/tools/solicitor/package-info.java | 12 +- .../curating/CurationReadingTest.java | 105 +++++ .../ScancodeComponentInfoAdapterTest.java | 175 +++++++ .../curations/array_of_curations.yaml | 13 + .../curation_with_all_fields_set.yaml | 13 + .../curation_with_fields_missing.yaml | 14 + .../0.1.0/origin.yaml | 5 + .../0.1.0/scancode.json | 252 ++++++++++ .../0.1.0/scancodeScan.completed | 0 .../0.1.0/sources.completed | 0 .../0.1.0/sources/NOTICE.txt | 1 + .../0.1.0/sources/pom.xml | 16 + .../com/devonfw/tools/test/SampleClass1.java | 20 + .../com/devonfw/tools/test/SampleClass2.java | 9 + .../scancodefileadapter/curations.yaml | 10 + .../scancodefileadapter/scancodeScan.sh | 24 + documentation/master-solicitor.asciidoc | 3 + 46 files changed, 2266 insertions(+), 495 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentContentProvider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoImpl.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultLicenseInfoImpl.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/UncuratedComponentInfoProvider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CurationList.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseInfoCuration.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/package-info.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/package-info.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/doc-files/componentinfo_classes.drawio.svg create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/package-info.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapter.java delete mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfo.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoPovider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/package-info.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/CurationReadingTest.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java create mode 100644 core/src/test/resources/curations/array_of_curations.yaml create mode 100644 core/src/test/resources/curations/curation_with_all_fields_set.yaml create mode 100644 core/src/test/resources/curations/curation_with_fields_missing.yaml create mode 100644 core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/origin.yaml create mode 100644 core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancode.json create mode 100644 core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancodeScan.completed create mode 100644 core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources.completed create mode 100644 core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/NOTICE.txt create mode 100644 core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/pom.xml create mode 100644 core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/src/main/java/com/devonfw/tools/test/SampleClass1.java create mode 100644 core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/src/main/java/com/devonfw/tools/test/SampleClass2.java create mode 100644 core/src/test/resources/scancodefileadapter/curations.yaml create mode 100644 core/src/test/resources/scancodefileadapter/scancodeScan.sh diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentContentProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentContentProvider.java new file mode 100644 index 00000000..b55093c8 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentContentProvider.java @@ -0,0 +1,23 @@ +package com.devonfw.tools.solicitor.componentinfo; + +/** + * Allows retrieving content from a (sub)path within the component given by its PackageURL. + */ +public interface ComponentContentProvider { + + /** + * Required (custom) prefix of the uri. + */ + public static final String PKG_CONTENT_SCHEMA_PREFIX = "pkgcontent:/"; + + /** + * Retrieves content from the (sub)path of a component given by the PackageUrl. + * + * @param packageUrl the packageUrl which references the component + * @param contentUri an uri giving the path to the content (relative to the root directory of the component) + * @return the found data. null if the uri does not start with {@link #PKG_CONTENT_SCHEMA_PREFIX} or + * nothing was found. + */ + String retrieveContent(String packageUrl, String contentUri); + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java index 4ffd7ede..8409a6c6 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java @@ -10,6 +10,13 @@ */ public interface ComponentInfo { + /** + * Get the packageUrl. + * + * @return the packageURL. This is an identifier for this object. + */ + String getPackageUrl(); + /** * Gets all copyrights. * @@ -25,11 +32,11 @@ public interface ComponentInfo { Collection getLicenses(); /** - * Gets the path to the notice file (if any) + * Gets the url to the notice file (if any) * - * @return path to the notice file + * @return url of the notice file */ - String getNoticeFilePath(); + String getNoticeFileUrl(); /** * Gets the contents of the notice file (if any) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapter.java index c984b044..c7d7295e 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoAdapter.java @@ -3,11 +3,12 @@ import org.springframework.core.annotation.Order; /** - * Adapter for reading {@link ComponentInfo} data for a package. There might be different beans in the application - * implementing this interface. Processing will the be done based on the given {@link Order}. The first non null results - * will be taken in this case. + * Adapter for reading {@link ComponentInfo} data for a package. This basically is a {@link ComponentInfoProvider} with + * the additional semantic that the {@link ComponentInfo} is "ready to use" i.e. any possible curations have been + * applied. There might be different beans in the application implementing this interface. Processing will the be done + * based on the given {@link Order}. The first non null results will be taken in this case. */ -public interface ComponentInfoAdapter { +public interface ComponentInfoAdapter extends ComponentInfoProvider { /** * Processing order to be used for the ComponentInfoAdapter. Use in {@link Order} annotation of the implementing @@ -16,15 +17,4 @@ public interface ComponentInfoAdapter { */ public static final int DEFAULT_PRIO = 200; - /** - * Retrieves the component information for a package identified by the given package URL. Returns the data as a - * {@link ComponentInfo} object. - * - * @param packageUrl The identifier of the package for which information is requested - * @return the data for the component. null is returned if no data is available, - * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no - * data available no exception will be thrown. Instead null will be returned in such a case. - */ - ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException; - } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 34288dd3..b8bbf6c9 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -19,8 +19,8 @@ /** * An {@link InventoryProcessor} which looks up license information for the found application components / packages at - * some external data source , like a scancode file store. If license information is found then the license, copyright - * and notice file information of the model will be replaced by the data obtained from this source. + * external data source by the use of {@link ComponentInfoAdapter}s. If license information is found then the license, + * copyright and notice file information of the model will be replaced by the data obtained from this source. * */ @Component @@ -90,7 +90,7 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { // Try to get component information from the available ComponentInfoAdapters ComponentInfo componentInfo = null; try { - for (ComponentInfoAdapter cia : this.componentInfoAdapters) { + for (ComponentInfoProvider cia : this.componentInfoAdapters) { componentInfo = cia.getComponentInfo(ac.getPackageUrl()); // stop querying further adapters if some info was returned if (componentInfo != null) { @@ -110,8 +110,8 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { ac.setTraceabilityNotes(formattedTraceabilityNotes); // Update the notice file URL and content if available - if (componentInfo.getNoticeFilePath() != null) { - ac.setNoticeFileUrl(componentInfo.getNoticeFilePath()); + if (componentInfo.getNoticeFileUrl() != null) { + ac.setNoticeFileUrl(componentInfo.getNoticeFileUrl()); } if (componentInfo.getNoticeFileContent() != null) { @@ -122,7 +122,7 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { if (componentInfo.getLicenses().size() > 0) { ac.removeAllRawLicenses(); for (LicenseInfo li : componentInfo.getLicenses()) { - addRawLicense(ac, li.getSpdxid(), li.getLicenseFilePath(), li.getGivenLicenseText(), ORIGIN_COMPONENTINFO); + addRawLicense(ac, li.getSpdxid(), li.getLicenseUrl(), li.getGivenLicenseText(), ORIGIN_COMPONENTINFO); } } else { LOG.info(LogMessages.COMPONENTINFO_NO_LICENSES.msg(), diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java new file mode 100644 index 00000000..f904a849 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java @@ -0,0 +1,21 @@ +package com.devonfw.tools.solicitor.componentinfo; + +/** + * Provides {@link ComponentInfo} for components given by their PackageURL. Subinterfaces further specify if the + * {@link ComponentInfo} is already curated or not. + * + */ +public interface ComponentInfoProvider { + + /** + * Retrieves the component information for a package identified by the given package URL. Returns the data as a + * {@link ComponentInfo} object. + * + * @param packageUrl The identifier of the package for which information is requested + * @return the data for the component. null is returned if no data is available, + * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no + * data available no exception will be thrown. Instead null will be returned in such a case. + */ + ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException; + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoImpl.java new file mode 100644 index 00000000..162dbf52 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoImpl.java @@ -0,0 +1,259 @@ +package com.devonfw.tools.solicitor.componentinfo; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Default POJO implementation of a {@link ComponentInfo}. + * + */ +public class DefaultComponentInfoImpl implements ComponentInfo { + + private String packageUrl; + + private Collection copyrights; + + private Collection licenses; + + private String noticeFileUrl; + + private String noticeFileContent; + + private String homepageUrl; + + private String sourceRepoUrl; + + private String packageDownloadUrl; + + private String sourceDownloadUrl; + + private String dataStatus; + + private List traceabilityNotes; + + /** + * The constructor. + */ + public DefaultComponentInfoImpl() { + + this.copyrights = new ArrayList<>(); + this.licenses = new ArrayList<>(); + this.traceabilityNotes = new ArrayList<>(); + } + + /** + * Copy-Constructor. Allows to construct an instance of the class from another {@link ComponentInfo} instance. Members + * are deep copied. Any changes to the new instance do not affect the original source. + * + * @param source the inctance to copy the data from + * + */ + public DefaultComponentInfoImpl(ComponentInfo source) { + + this(); + this.packageUrl = source.getPackageUrl(); + this.dataStatus = source.getDataStatus(); + this.homepageUrl = source.getHomepageUrl(); + this.noticeFileContent = source.getNoticeFileContent(); + this.noticeFileUrl = source.getNoticeFileUrl(); + this.packageDownloadUrl = source.getPackageDownloadUrl(); + this.sourceDownloadUrl = source.getSourceDownloadUrl(); + this.sourceRepoUrl = source.getSourceRepoUrl(); + for (String copyright : source.getCopyrights()) { + addCopyright(copyright); + } + for (String traceabilityNote : source.getTraceabilityNotes()) { + addTraceabillityNote(traceabilityNote); + } + for (LicenseInfo licenseInfo : source.getLicenses()) { + addLicense(licenseInfo); + } + } + + /** + * @param packageUrl new value of {@link #getPackageUrl}. + */ + public void setPackageUrl(String packageUrl) { + + this.packageUrl = packageUrl; + } + + /** + * @param noticeFileUrl new value of {@link #getNoticeFileUrl}. + */ + public void setNoticeFileUrl(String noticeFileUrl) { + + this.noticeFileUrl = noticeFileUrl; + } + + /** + * @param noticeFileContent new value of {@link #getNoticeFileContent}. + */ + public void setNoticeFileContent(String noticeFileContent) { + + this.noticeFileContent = noticeFileContent; + } + + /** + * @param homepageUrl new value of {@link #getHomepageUrl}. + */ + public void setHomepageUrl(String homepageUrl) { + + this.homepageUrl = homepageUrl; + } + + /** + * @param sourceRepoUrl new value of {@link #getSourceRepoUrl}. + */ + public void setSourceRepoUrl(String sourceRepoUrl) { + + this.sourceRepoUrl = sourceRepoUrl; + } + + /** + * @param packageDownloadUrl new value of {@link #getPackageDownloadUrl}. + */ + public void setPackageDownloadUrl(String packageDownloadUrl) { + + this.packageDownloadUrl = packageDownloadUrl; + } + + /** + * @param sourceDownloadUrl new value of {@link #getSourceDownloadUrl}. + */ + public void setSourceDownloadUrl(String sourceDownloadUrl) { + + this.sourceDownloadUrl = sourceDownloadUrl; + } + + /** + * @param dataStatus new value of {@link #getDataStatus}. + */ + public void setDataStatus(String dataStatus) { + + this.dataStatus = dataStatus; + } + + @Override + public Collection getCopyrights() { + + return Collections.unmodifiableCollection(this.copyrights); + } + + /** + * Clears the collection of copyrights. + */ + public void clearCopyrights() { + + this.copyrights = new ArrayList<>(); + } + + /** + * Adds a copyright string to the list of already existing copyrights. + * + * @param copyright the copyrigt sring to add + */ + public void addCopyright(String copyright) { + + this.copyrights.add(copyright); + } + + @Override + public Collection getLicenses() { + + return Collections.unmodifiableCollection(this.licenses); + } + + /** + * Clears the collection of licenses. + */ + public void clearLicenses() { + + this.licenses = new ArrayList<>(); + } + + /** + * Adds a {@link LicenseInfo} instance to the collection of licenses. + * + * @param licenseInfo the license to add + */ + public void addLicense(LicenseInfo licenseInfo) { + + this.licenses.add(new DefaultLicenseInfoImpl(licenseInfo)); + } + + @Override + public String getPackageUrl() { + + return this.packageUrl; + } + + @Override + public String getNoticeFileUrl() { + + return this.noticeFileUrl; + } + + @Override + public String getNoticeFileContent() { + + return this.noticeFileContent; + } + + @Override + public String getHomepageUrl() { + + return this.homepageUrl; + } + + @Override + public String getSourceRepoUrl() { + + return this.sourceRepoUrl; + } + + @Override + public String getPackageDownloadUrl() { + + return this.packageDownloadUrl; + } + + @Override + public String getSourceDownloadUrl() { + + return this.sourceDownloadUrl; + } + + @Override + public String getDataStatus() { + + return this.dataStatus; + } + + @Override + public List getTraceabilityNotes() { + + return Collections.unmodifiableList(this.traceabilityNotes); + } + + /** + * Clears the list of tracebilityNotes. + */ + public void clearTraceabilityNotes() { + + this.traceabilityNotes = new ArrayList<>(); + } + + /** + * Appends a new traceabilityNote to the already existing list. + * + * @param traceabilityNote the note to append + */ + public void addTraceabillityNote(String traceabilityNote) { + + this.traceabilityNotes.add(traceabilityNote); + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultLicenseInfoImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultLicenseInfoImpl.java new file mode 100644 index 00000000..813d401e --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultLicenseInfoImpl.java @@ -0,0 +1,77 @@ +package com.devonfw.tools.solicitor.componentinfo; + +/** + * Default POJO implementation of the {@link LicenseInfo} interface. + * + */ +public class DefaultLicenseInfoImpl implements LicenseInfo { + + private String spdxid; + + private String licenseUrl; + + private String givenLicenseText; + + /** + * The constructor. + */ + public DefaultLicenseInfoImpl() { + + } + + /** + * Copy constructor. Creates the new instance by copying the data of the given source. + * + * @param source the source instance to copy from + */ + public DefaultLicenseInfoImpl(LicenseInfo source) { + + this(); + this.spdxid = source.getSpdxid(); + this.licenseUrl = source.getLicenseUrl(); + this.givenLicenseText = source.getGivenLicenseText(); + } + + @Override + public String getSpdxid() { + + return this.spdxid; + } + + @Override + public String getLicenseUrl() { + + return this.licenseUrl; + } + + @Override + public String getGivenLicenseText() { + + return this.givenLicenseText; + } + + /** + * @param spdxid new value of {@link #getSpdxid}. + */ + public void setSpdxId(String spdxid) { + + this.spdxid = spdxid; + } + + /** + * @param licenseUrl new value of {@link #getLicenseUrl}. + */ + public void setLicenseUrl(String licenseUrl) { + + this.licenseUrl = licenseUrl; + } + + /** + * @param givenLicenseText new value of {@link #getGivenLicenseText}. + */ + public void setGivenLicenseText(String givenLicenseText) { + + this.givenLicenseText = givenLicenseText; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java index e46a965a..8a47d8d2 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/LicenseInfo.java @@ -12,9 +12,9 @@ public interface LicenseInfo { String getSpdxid(); /** - * @return licenseFilePath + * @return licenseUrl */ - String getLicenseFilePath(); + String getLicenseUrl(); /** * @return the given license text (might be null) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java new file mode 100644 index 00000000..95148cb6 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java @@ -0,0 +1,21 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; + +/** + * A {@link ComponentInfoCurator} curates the {@link ComponentInfo} data. + * + */ +public interface ComponentInfoCurator { + + /** + * Curates the given {@link ComponentInfo}. + * + * @param componentInfo the componentInfo to curate + * @return the curated component info or - if no curations are done - the incoming object + * @throws ComponentInfoAdapterException if the curations could not be read + */ + ComponentInfo curate(ComponentInfo componentInfo) throws ComponentInfoAdapterException; + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java new file mode 100644 index 00000000..ca7d7e51 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java @@ -0,0 +1,96 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.componentinfo.ComponentContentProvider; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.DefaultComponentInfoImpl; +import com.devonfw.tools.solicitor.componentinfo.DefaultLicenseInfoImpl; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseInfoCuration; + +/** + * This {@link ComponentInfoCurator} curates the {@link ComponentInfo} dat based on the data obtained from a + * {@link SingleFileCurationProvider}. + * + */ +@Component +public class ComponentInfoCuratorImpl implements ComponentInfoCurator { + + private static final Logger LOG = LoggerFactory.getLogger(ComponentInfoCuratorImpl.class); + + private ComponentContentProvider componentContentProvider; + + private CurationProvider curationProvider; + + /** + * The constructor. + * + * @param curationProvider the curation provider used to get the curations + * @param componentContentProvider the provider used for loading referenced subpath data from the component + */ + @Autowired + public ComponentInfoCuratorImpl(CurationProvider curationProvider, + ComponentContentProvider componentContentProvider) { + + this.curationProvider = curationProvider; + this.componentContentProvider = componentContentProvider; + } + + /** + * Checks for the existence of curations for the given package via the {@link CurationProvider}. If curations exist + * then the a new curated {@link ComponentInfo} instance will be created from the incoming uncurated + * {@link ComponentInfo} and the curations. + * + * @param componentInfo the componentInfo to curate + * @return the curated component info + * @throws ComponentInfoAdapterException if the curations could not be read + */ + @Override + public ComponentInfo curate(ComponentInfo componentInfo) throws ComponentInfoAdapterException { + + ComponentInfoCuration foundCuration = this.curationProvider.findCurations(componentInfo.getPackageUrl()); + if (foundCuration != null) { + DefaultComponentInfoImpl componentInfoImpl = new DefaultComponentInfoImpl(componentInfo); + applyFoundCurations(componentInfoImpl, foundCuration); + return componentInfoImpl; + } else { + return componentInfo; + } + + } + + private void applyFoundCurations(DefaultComponentInfoImpl componentInfo, ComponentInfoCuration curation) { + + if (curation.getCopyrights() != null) { + componentInfo.clearCopyrights(); + for (String copyright : curation.getCopyrights()) { + componentInfo.addCopyright(copyright); + } + } + if (curation.getUrl() != null) { + componentInfo.setSourceRepoUrl(curation.getUrl()); + } + if (curation.getLicenses() != null) { + componentInfo.clearLicenses(); + for (LicenseInfoCuration licenseCuration : curation.getLicenses()) { + String license = licenseCuration.getLicense(); + String url = licenseCuration.getUrl(); + String givenLicenseText = null; + if (url != null) { + givenLicenseText = this.componentContentProvider.retrieveContent(componentInfo.getPackageUrl(), url); + } + DefaultLicenseInfoImpl licenseInfo = new DefaultLicenseInfoImpl(); + licenseInfo.setSpdxId(license); + licenseInfo.setLicenseUrl(url); + licenseInfo.setGivenLicenseText(givenLicenseText); + componentInfo.addLicense(licenseInfo); + } + } + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java new file mode 100644 index 00000000..147506e5 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java @@ -0,0 +1,71 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; + +/** + * A {@link ComponentInfoAdapter} which takes uncurated {@link ComponentInfo} data from the configuret + * {@link UncuratedComponentInfoProvider} and curates it via the given {@link ComponentInfoCurator}. + * + */ +public class CuratingComponentInfoAdapter implements ComponentInfoAdapter { + + private UncuratedComponentInfoProvider uncuratedComponentInfoProvider; + + private ComponentInfoCurator componentInfoCurator; + + /** + * The constructor. + * + * @param uncuratedComponentInfoProvider the provider of the uncurated {@link ComponentInfo} data + * @param componentInfoCurator the curator to take + */ + public CuratingComponentInfoAdapter(UncuratedComponentInfoProvider uncuratedComponentInfoProvider, + ComponentInfoCurator componentInfoCurator) { + + this.uncuratedComponentInfoProvider = uncuratedComponentInfoProvider; + this.componentInfoCurator = componentInfoCurator; + } + + /** + * Retrieves the component information and curations for a package identified by the given package URL. Returns the + * data as a {@link ComponentInfo} object. + * + * @param packageUrl The identifier of the package for which information is requested + * @return the data derived from the scancode results after applying any defined curations. null is + * returned if no data is available, + * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no + * data available no exception will be thrown. Instead null will be return in such a case. + */ + @Override + public ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException { + + if (isFeatureActive()) { + + ComponentInfo componentInfo = this.uncuratedComponentInfoProvider.getComponentInfo(packageUrl); + if (componentInfo == null) { + return null; + } + componentInfo = this.componentInfoCurator.curate(componentInfo); + + return componentInfo; + + } else { + return null; + } + + } + + /** + * Returns if the adapter should be active. Default implementation returns true. Subclasses might + * override this to enable/disable this functionality depending on some configuration; + * + * @return true if the adapter shall be active, false otherwise. + */ + protected boolean isFeatureActive() { + + return true; + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java new file mode 100644 index 00000000..a916d2c7 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java @@ -0,0 +1,22 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; + +/** + * Provides curation data for {@link ComponentInfo} data. + * + */ +public interface CurationProvider { + + /** + * Return the curation data for a given package + * + * @param packageUrl identifies the package + * @return the curation data if it exists or null if no curations exist for the package. + * @throws ComponentInfoAdapterException if something unexpected happens + */ + ComponentInfoCuration findCurations(String packageUrl) throws ComponentInfoAdapterException; + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java new file mode 100644 index 00000000..10c5845c --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java @@ -0,0 +1,113 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.CurationList; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +/** + * Implementation of the {@link CurationProvider} interface which reads curation data for all packages from a single + * file. + * + */ +@Component +public class SingleFileCurationProvider implements CurationProvider { + private static final Logger LOG = LoggerFactory.getLogger(SingleFileCurationProvider.class); + + private static final ObjectMapper yamlMapper; + + static { + yamlMapper = new ObjectMapper(new YAMLFactory()); + yamlMapper.findAndRegisterModules(); + } + + private String curationsFileName; + + private boolean curationsExistenceLogged; + + private AllKindsPackageURLHandler packageURLHandler; + + /** + * The constructor. + * + * @param packageURLHandler the packageURLHandler for handling packageURLs. + */ + @Autowired + public SingleFileCurationProvider(AllKindsPackageURLHandler packageURLHandler) { + + this.packageURLHandler = packageURLHandler; + } + + /** + * Return the curation data for a given package + * + * @param packageUrl identifies the package + * @return the curation data if it existes or null if no curations exist for the package. + * @throws ComponentInfoAdapterException if something unexpected happens + */ + @Override + public ComponentInfoCuration findCurations(String packageUrl) throws ComponentInfoAdapterException { + + ComponentInfoCuration foundCuration = null; + + String packagePathPart = this.packageURLHandler.pathFor(packageUrl); + + File curationsFile = new File(this.curationsFileName); + if (!curationsFile.exists()) { + if (!this.curationsExistenceLogged) { + // log only once + this.curationsExistenceLogged = true; + LOG.info(LogMessages.CURATIONS_NOT_EXISTING.msg(), this.curationsFileName); + } + } else { + if (!this.curationsExistenceLogged) { + // log only once + this.curationsExistenceLogged = true; + LOG.info(LogMessages.CURATIONS_PROCESSING.msg(), this.curationsFileName); + } + try (InputStream isc = new FileInputStream(this.curationsFileName)) { + + CurationList curationList = yamlMapper.readValue(isc, CurationList.class); + + for (ComponentInfoCuration curation : curationList.getArtifacts()) { + String component = curation.getName(); + if (component.equals(packagePathPart)) { + foundCuration = curation; + break; + } + } + + } catch (IOException e) { + throw new ComponentInfoAdapterException("Could not read Curations JSON", e); + } + + } + return foundCuration; + } + + /** + * Sets curationsFileName. + * + * @param curationsFileName new value of curationsFileName. + */ + @Value("${solicitor.scancode.curations-filename}") + public void setCurationsFileName(String curationsFileName) { + + this.curationsFileName = curationsFileName; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/UncuratedComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/UncuratedComponentInfoProvider.java new file mode 100644 index 00000000..3c0bf998 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/UncuratedComponentInfoProvider.java @@ -0,0 +1,12 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoProvider; + +/** + * A {@link ComponentInfoProvider} which provides uncurated {@link ComponentInfo}s. + * + */ +public interface UncuratedComponentInfoProvider extends ComponentInfoProvider { + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java new file mode 100644 index 00000000..bd84c95f --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java @@ -0,0 +1,111 @@ +package com.devonfw.tools.solicitor.componentinfo.curation.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Component curation info. + * + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ComponentInfoCuration { + + private String name; + + private String note; + + private String url; + + private List copyrights; + + private List licenses; + + /** + * The constructor. + */ + public ComponentInfoCuration() { + + } + + /** + * @return name + */ + public String getName() { + + return this.name; + } + + /** + * @param name new value of {@link #getName}. + */ + public void setName(String name) { + + this.name = name; + } + + /** + * @return note + */ + public String getNote() { + + return this.note; + } + + /** + * @param note new value of {@link #getNote}. + */ + public void setNote(String note) { + + this.note = note; + } + + /** + * @return url + */ + public String getUrl() { + + return this.url; + } + + /** + * @param url new value of {@link #getUrl}. + */ + public void setUrl(String url) { + + this.url = url; + } + + /** + * @return copyrights + */ + public List getCopyrights() { + + return this.copyrights; + } + + /** + * @param copyrights new value of {@link #getCopyrights}. + */ + public void setCopyrights(List copyrights) { + + this.copyrights = copyrights; + } + + /** + * @return licenses + */ + public List getLicenses() { + + return this.licenses; + } + + /** + * @param licenses new value of {@link #getLicenses}. + */ + public void setLicenses(List licenses) { + + this.licenses = licenses; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CurationList.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CurationList.java new file mode 100644 index 00000000..daa46e01 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CurationList.java @@ -0,0 +1,37 @@ +package com.devonfw.tools.solicitor.componentinfo.curation.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Holds a list of {@link ComponentInfoCuration}s. + * + */ +public class CurationList { + + List artifacts = new ArrayList<>(); + + /** + * @return artifacts + */ + public List getArtifacts() { + + return this.artifacts; + } + + /** + * @param artifacts new value of {@link #getArtifacts}. + */ + public void setArtifacts(List artifacts) { + + this.artifacts = artifacts; + } + + /** + * The constructor. + */ + public CurationList() { + + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseInfoCuration.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseInfoCuration.java new file mode 100644 index 00000000..e41620ff --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseInfoCuration.java @@ -0,0 +1,54 @@ +package com.devonfw.tools.solicitor.componentinfo.curation.model; + +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * License curation info. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class LicenseInfoCuration { + + private String license; + + private String url; + + /** + * The constructor. + */ + public LicenseInfoCuration() { + + } + + /** + * @return license + */ + public String getLicense() { + + return this.license; + } + + /** + * @param license new value of {@link #getLicense}. + */ + public void setLicense(String license) { + + this.license = license; + } + + /** + * @return url + */ + public String getUrl() { + + return this.url; + } + + /** + * @param url new value of {@link #getUrl}. + */ + public void setUrl(String url) { + + this.url = url; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/package-info.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/package-info.java new file mode 100644 index 00000000..423f941b --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/package-info.java @@ -0,0 +1,4 @@ +/** + * POJO classes holding curation data. + */ +package com.devonfw.tools.solicitor.componentinfo.curation.model; \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/package-info.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/package-info.java new file mode 100644 index 00000000..263c1d2d --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/package-info.java @@ -0,0 +1,8 @@ +/** + * Interfaces and classes related to applying curation data to the uncurated + * {@link com.devonfw.tools.solicitor.componentinfo.ComponentInfo} data. + *

        + * See {@link com.devonfw.tools.solicitor.componentinfo} for an overview diagram of interfaces/classes. + *

        + */ +package com.devonfw.tools.solicitor.componentinfo.curation; \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/doc-files/componentinfo_classes.drawio.svg b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/doc-files/componentinfo_classes.drawio.svg new file mode 100644 index 00000000..07283864 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/doc-files/componentinfo_classes.drawio.svg @@ -0,0 +1,3 @@ + + +
        Curation data
        Curation data
        ComponentInfo data
        ComponentInfo da...
        «interface»
        ComponentInfo
        «interface»...
        «interface»
        LicenseInfo
        «interface»...
        DefaultComponentInfoImpl
        DefaultComponentInfoImpl
        DefaultLicenseInfoImpl
        DefaultLicenseInfoImpl

        <<Interface>>
        ComponentInfoProvider


        + getComponentInfo(packageUrl): ComponentInfo


        <<Interface>>...
        «interface»
        ComponentInfoAdapter
        «interface»...
        Extends
        Extends
        use
        use


        ComponentInfoInventoryProcessor

        ComponentInfoInventoryProcessor
        «interface»
        UncuratedComponentInfoProvider
        «interface»...
        Extends
        Extends


        CuratingComponentInfoAdapter

        CuratingComponentInfoAdapter

        <<Interface>>
        ComponentInfoCurator


        + curate(componentInfo): ComponentInfo

        <<Interface>>...
        Use
        Use
        Use
        Use


        ComponentInfoCuratorImpl

        ComponentInfoCuratorImpl

        <<Interface>>
        CurationProvider


        + findCuration(packageUrl): ComponentInfoCuration

        <<Interface>>...
        Use
        Use


        SingleFileCurationProvider

        SingleFileCurationProvider


        ComponentInfoCuration

        ComponentInfoCuration


        LicenseInfoCuration

        LicenseInfoCuration


        ScancodeComponentInfoAdapter

        ScancodeComponentInfoAdapter
        Extends
        Extends


        UncuratedScancodeComponentInfoProvider

        UncuratedScancodeComponentInfoProvider
        Use
        Use

        <<Interface>>
        ScancodeRawComponentInfoProvider


        + readScancodeData(packageUrl): ScancodeRawComponentInfo

        + pkgContentUriFromPath(packageUrl, path): String

        + isLocalContentPath(packageUrl, path): boolean

        <<Interface>>...


        FileScancodeRawComponentInfoProvider

        FileScancodeRawComponentInfoProvider...
        ScancodeComponentInfo
        ScancodeComponentInfo
        ScancodeLicenseInfo
        ScancodeLicenseInfo
        Package:
        com.devonfw.tools.solictor.componentinfo.scancode
        Package:com.devonfw.tools.solictor.componentinfo.sc...
        Package:
        com.devonfw.tools.solictor.componentinfo
        Package:com.devonfw.tools.solictor.compo...
        Package:
        com.devonfw.tools.solictor.componentinfo.curation(.model)
        Package:com.devonfw.tools.solictor.componentinfo.curatio...
        Classes with thick line are instantiated as beans
        Classes with thick line are instantiated as b...

        <<Interface>>
        ComponentContentProvider


        + retrieveContent(packageUrl, path) : String

        <<Interface>>...
        Extends
        Extends
        Use
        Use
        Viewer does not support full SVG 1.1
        \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/package-info.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/package-info.java new file mode 100644 index 00000000..a5c9d554 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/package-info.java @@ -0,0 +1,14 @@ +/** + * Main interfaces and classes for enriching the data of + * {@link com.devonfw.tools.solicitor.model.inventory.ApplicationComponent}s with + * {@link com.devonfw.tools.solicitor.componentinfo.ComponentInfo}/{@link com.devonfw.tools.solicitor.componentinfo.LicenseInfo}. + * This data might e.g. originate from a deep license scan done with + * ScanCode. + *

        + * The main interfaces / classes / spring beans of this package (and subpackages) are shown in the following diagram: + *

        + * Interfaces / classes and Spring Beans of the componentinfo
+ * packages + *

        + */ +package com.devonfw.tools.solicitor.componentinfo; \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java new file mode 100644 index 00000000..dcd7fd79 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java @@ -0,0 +1,177 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.IOHelper; +import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +/** + * Provider for {@link ScancodeRawComponentInfo} which reads the scancode (and supplementary data) from the file system. + * + */ +@Component +public class FileScancodeRawComponentInfoProvider implements ScancodeRawComponentInfoPovider { + + /** + * The directory within the component root directory which contains the sources / the content + */ + private static final String SOURCES_DIR = "sources/"; + + private static final Logger LOG = LoggerFactory.getLogger(FileScancodeRawComponentInfoProvider.class); + + private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + + private String repoBasePath; + + private AllKindsPackageURLHandler packageURLHandler; + + private DirectUrlWebContentProvider contentProvider; + + /** + * The constructor. + * + * @param contentProvider content provider for accessing source files of the packag + * @param packageURLHandler handler to deal with PackageURLs. + */ + @Autowired + public FileScancodeRawComponentInfoProvider(DirectUrlWebContentProvider contentProvider, + AllKindsPackageURLHandler packageURLHandler) { + + this.contentProvider = contentProvider; + this.packageURLHandler = packageURLHandler; + + } + + /** + * Sets repoBasePath. + * + * @param repoBasePath new value of repoBasePath. + */ + @Value("${solicitor.scancode.repo-base-path}") + public void setRepoBasePath(String repoBasePath) { + + this.repoBasePath = repoBasePath; + } + + /** + * Retrieve the {@link ScancodeRawComponentInfo} for the package given by its PackageURL. + * + * @param packageUrl the identifier for the package + * @return the raw data base on scancode and supplemental data. null if no data is available. + * @throws ComponentInfoAdapterException is something unexpected happens + */ + @Override + public ScancodeRawComponentInfo readScancodeData(String packageUrl) throws ComponentInfoAdapterException { + + String packagePathPart = this.packageURLHandler.pathFor(packageUrl); + String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json"; + + File scanCodeFile = new File(path); + if (!scanCodeFile.exists()) { + LOG.debug("No Scancode info available for PackageURL '{}'", packageUrl); + return null; + } + String scancodeString; + LOG.debug("Found Scancode info for PackageURL '{}'", packageUrl); + try (InputStream is = new FileInputStream(scanCodeFile)) { + scancodeString = IOHelper.readStringFromInputStream(is); + } catch (IOException e) { + throw new ComponentInfoAdapterException("Could not read Scancode JSON", e); + } + + ScancodeRawComponentInfo result = new ScancodeRawComponentInfo(); + result.rawScancodeResult = scancodeString; + addOriginData(packageUrl, result); + return result; + } + + /** + * Adds the data about the origin of the package which is (optionally) contained in file "origin.yaml" + * + * @param packageUrl the identifier of the package + * @param componentScancodeInfos the componentScancodeInfos to add the origin data to + * @throws ComponentInfoAdapterException if there was an error when reading the file + */ + private void addOriginData(String packageUrl, ScancodeRawComponentInfo componentScancodeInfos) + throws ComponentInfoAdapterException { + + String packagePathPart = this.packageURLHandler.pathFor(packageUrl); + String path = this.repoBasePath + "/" + packagePathPart + "/origin.yaml"; + + File originFile = new File(path); + if (!originFile.exists()) { + LOG.debug("No origin info available for PackageURL '{}'", packageUrl); + return; + } + LOG.debug("Found origin info for PackageURL '{}'", packageUrl); + + try (InputStream is = new FileInputStream(originFile)) { + + JsonNode originYaml = yamlMapper.readTree(is); + + String sourceDownloadUrl = originYaml.get("sourceDownloadUrl") != null + ? originYaml.get("sourceDownloadUrl").asText() + : null; + String packageDownloadUrl = originYaml.get("packageDownloadUrl") != null + ? originYaml.get("packageDownloadUrl").asText() + : null; + String note = originYaml.get("note") != null ? originYaml.get("note").asText() : null; + if (note != null) { + LOG.debug("Note: " + note); + } + + componentScancodeInfos.sourceDownloadUrl = sourceDownloadUrl; + componentScancodeInfos.packageDownloadUrl = packageDownloadUrl; + + } catch (IOException e) { + throw new ComponentInfoAdapterException("Could not read origin.yaml", e); + } + } + + @Override + public String retrieveContent(String packageUrl, String fileUri) { + + if (!fileUri.startsWith(PKG_CONTENT_SCHEMA_PREFIX)) { + return null; + } + if (fileUri.contains("..")) { + // prevent directory traversal + LOG.debug("Suspicious file traversal in URI '{}', returning null", fileUri); + return null; + } + String pathWithoutPrefix = fileUri.substring(PKG_CONTENT_SCHEMA_PREFIX.length()); + String directUrl = "file:" + this.repoBasePath + "/" + this.packageURLHandler.pathFor(packageUrl) + "/" + + SOURCES_DIR + pathWithoutPrefix; + return this.contentProvider.getContentForUri(directUrl).getContent(); + } + + @Override + public boolean isLocalContentPath(String packageUrl, String path) { + + return (path != null && path.startsWith(SOURCES_DIR)); + } + + @Override + public String pkgContentUriFromPath(String packageUrl, String path) { + + if (!isLocalContentPath(packageUrl, path)) { + throw new IllegalArgumentException("'" + path + "' is not a valid path to content within the package"); + } + return PKG_CONTENT_SCHEMA_PREFIX + path.substring(SOURCES_DIR.length()); + + } +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index b3824a45..80038865 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -1,5 +1,6 @@ package com.devonfw.tools.solicitor.componentinfo.scancode; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -37,9 +38,9 @@ public class ScancodeLicenseInfo implements LicenseInfo { private double licenseScore; /** - * Path to the license file. + * Url of the license file. */ - private String licenseFilePath; + private String licenseUrl; /** * Text of license. @@ -58,11 +59,11 @@ public class ScancodeLicenseInfo implements LicenseInfo { * @param spdxid the spdx id * @param defaultUrl the default URL of the license text * @param licenseScore the score for the license - * @param licenseFilePath the path to the license file + * @param licenseUrl the path to the license file * @param givenLicenseText the given license text (might be null) * @param licenseFileScore the score of the license file - number of lines with license info */ - public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseFilePath, + public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseUrl, String givenLicenseText, int licenseFileScore) { super(); @@ -70,11 +71,11 @@ public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double l this.spdxid = spdxid; this.licenseScore = licenseScore; if (licenseFileScore >= ScancodeComponentInfo.this.minLicensefileNumberOfLines) { - this.licenseFilePath = licenseFilePath; + this.licenseUrl = licenseUrl; this.licenseFileScore = licenseFileScore; this.givenLicenseText = givenLicenseText; } else { - this.licenseFilePath = defaultUrl; + this.licenseUrl = defaultUrl; this.licenseFileScore = 0; } } @@ -105,12 +106,12 @@ public double getLicenseScore() { } /** - * @return licenseFilePath + * @return licenseUrl */ @Override - public String getLicenseFilePath() { + public String getLicenseUrl() { - return this.licenseFilePath; + return this.licenseUrl; } /** @@ -155,11 +156,11 @@ public void setLicenseScore(double licenseScore) { } /** - * @param licenseFilePath new value of {@link #getLicenseFilePath}. + * @param licenseUrl new value of {@link #getLicenseUrl}. */ - public void setLicenseFilePath(String licenseFilePath) { + public void setLicenseUrl(String licenseUrl) { - this.licenseFilePath = licenseFilePath; + this.licenseUrl = licenseUrl; } /** @@ -186,9 +187,11 @@ public void setLicenseFileScore(int licenseFileScore) { private SortedMap licenses = new TreeMap<>(); + private String packageUrl; + private double noticeFileScore = 0; - private String noticeFilePath = null; + private String noticeFileUrl = null; private String noticeFileContent; @@ -206,7 +209,7 @@ public void setLicenseFileScore(int licenseFileScore) { private String dataStatus; - private List traceabilityNotes; + private List traceabilityNotes = new ArrayList<>(); /** * The constructor. @@ -269,7 +272,7 @@ public void addLicense(String licenseId, String licenseName, String licenseDefau ScancodeLicenseInfo existingLicenseInfo = this.licenses.get(licenseId); double resultingScore = Math.max(existingLicenseInfo.getLicenseScore(), score); - String resultingFilePath = existingLicenseInfo.getLicenseFilePath(); + String resultingFilePath = existingLicenseInfo.getLicenseUrl(); String resultingGivenText = existingLicenseInfo.getGivenLicenseText(); int resultingFileScore = existingLicenseInfo.getLicenseFileScore(); if (fileScore > existingLicenseInfo.getLicenseFileScore()) { @@ -308,16 +311,16 @@ public void clearLicenses() { } /** - * Stores the path to a NOTICE file if the score exceeds the minimum required score and is higher than the score of - * already existing information. + * Stores the reference to a NOTICE file if the score exceeds the minimum required score and is higher than the score + * of already existing information. * - * @param path path to the NOTICE file + * @param url reference to the NOTICE file * @param score score of the file */ - public void addNoticeFilePath(String path, double score) { + public void addNoticeFileUrl(String url, double score) { if (score > this.noticeFileScore && score >= MIN_NOTICEFILE_PERCENTAGE) { - this.noticeFilePath = path; + this.noticeFileUrl = url; this.noticeFileScore = score; } } @@ -331,14 +334,14 @@ public void setNoticeFileContent(String noticeFileContent) { } /** - * Gets the path to the notice file (if any). + * Gets the referennce to the notice file (if any). * - * @return path to the notice file + * @return reference to the notice file */ @Override - public String getNoticeFilePath() { + public String getNoticeFileUrl() { - return this.noticeFilePath; + return this.noticeFileUrl; } @Override @@ -466,4 +469,19 @@ public void setTraceabilityNotes(List traceabilityNotes) { this.traceabilityNotes = traceabilityNotes; } + + @Override + public String getPackageUrl() { + + return this.packageUrl; + } + + /** + * @param packageUrl new value of {@link #getPackageUrl}. + */ + public void setPackageUrl(String packageUrl) { + + this.packageUrl = packageUrl; + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapter.java new file mode 100644 index 00000000..92cebae3 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapter.java @@ -0,0 +1,67 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter; +import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCurator; +import com.devonfw.tools.solicitor.componentinfo.curation.CuratingComponentInfoAdapter; + +/** + * Adapter for providing curated {@link ComponentInfo} based on Scancode data. + */ +@Component +@Order(ComponentInfoAdapter.DEFAULT_PRIO) +public class ScancodeComponentInfoAdapter extends CuratingComponentInfoAdapter { + + private static final Logger LOG = LoggerFactory.getLogger(ScancodeComponentInfoAdapter.class); + + private boolean featureFlag = false; + + private boolean featureLogged = false; + + /** + * The constructor. + * + * @param uncuratedScancodeComponentInfoProvider provider for uncurated data originating from scancode data + * @param componentInfoCurator the curator to use + */ + @Autowired + public ScancodeComponentInfoAdapter(UncuratedScancodeComponentInfoProvider uncuratedScancodeComponentInfoProvider, + ComponentInfoCurator componentInfoCurator) { + + super(uncuratedScancodeComponentInfoProvider, componentInfoCurator); + } + + /** + * Sets the feature flag for activating/deactivating this feature. + * + * @param featureFlag the flag + */ + @Value("${solicitor.feature-flag.scancode}") + public void setFeatureFlag(boolean featureFlag) { + + this.featureFlag = featureFlag; + } + + @Override + protected boolean isFeatureActive() { + + if (!this.featureLogged) { + if (this.featureFlag) { + LOG.warn(LogMessages.SCANCODE_PROCESSOR_STARTING.msg()); + } else { + LOG.info(LogMessages.SCANCODE_FEATURE_DEACTIVATED.msg()); + } + this.featureLogged = true; + } + return this.featureFlag; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java deleted file mode 100644 index 4828ff67..00000000 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeFileAdapter.java +++ /dev/null @@ -1,442 +0,0 @@ -package com.devonfw.tools.solicitor.componentinfo.scancode; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - -import com.devonfw.tools.solicitor.common.LogMessages; -import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; -import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; -import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; -import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter; -import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; - -/** - * Adapter for reading Scancode information for a package from a file, applying any given curations and returning the - * information as a {@link ComponentInfo} object. - */ -@Component -@Order(ComponentInfoAdapter.DEFAULT_PRIO) -public class ScancodeFileAdapter implements ComponentInfoAdapter { - - private static final Logger LOG = LoggerFactory.getLogger(ScancodeFileAdapter.class); - - private static final ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); - - private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); - - private String repoBasePath; - - private String curationsFileName; - - private double minLicenseScore; - - private int minLicensefileNumberOfLines; - - private boolean curationsExistenceLogged; - - private boolean featureFlag = false; - - private boolean featureLogged = false; - - private double licenseToTextRatioToTakeCompleteFile = 90; - - @Autowired - private AllKindsPackageURLHandler packageURLHandler; - - @Autowired - private DirectUrlWebContentProvider contentProvider; - - /** - * The constructor. - */ - - public ScancodeFileAdapter() { - - } - - /** - * Sets the feature flag for activating/deactivating this feature. - * - * @param featureFlag the flag - */ - @Value("${solicitor.feature-flag.scancode}") - public void setFeatureFlag(boolean featureFlag) { - - this.featureFlag = featureFlag; - } - - /** - * Sets repoBasePath. - * - * @param repoBasePath new value of repoBasePath. - */ - @Value("${solicitor.scancode.repo-base-path}") - public void setRepoBasePath(String repoBasePath) { - - this.repoBasePath = repoBasePath; - } - - /** - * Sets curationsFileName. - * - * @param curationsFileName new value of curationsFileName. - */ - @Value("${solicitor.scancode.curations-filename}") - public void setCurationsFileName(String curationsFileName) { - - this.curationsFileName = curationsFileName; - } - - /** - * Sets minLicenseScore. - * - * @param minLicenseScore new value of minLicenseScore. - */ - @Value("${solicitor.scancode.min-license-score}") - public void setMinLicenseScore(double minLicenseScore) { - - this.minLicenseScore = minLicenseScore; - } - - /** - * Sets minLicensefileNumberOfLines. - * - * @param minLicensefileNumberOfLines new value of minLicensefileNumberOfLines. - */ - @Value("${solicitor.scancode.min-licensefile-number-of-lines}") - public void setMinLicensefileNumberOfLines(int minLicensefileNumberOfLines) { - - this.minLicensefileNumberOfLines = minLicensefileNumberOfLines; - } - - /** - * Retrieves the Scancode information and curations for a package identified by the given package URL. Returns the - * data as a {@link ScancodeComponentInfo} object. - * - * @param packageUrl The identifier of the package for which information is requested - * @return the data derived from the scancode results after applying any defined curations. null is - * returned if no data is available, - * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no - * data available no exception will be thrown. Instead null will be return in such a case. - */ - @Override - public ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException { - - if (isFeatureActive()) { - - ScancodeComponentInfo componentScancodeInfos = determineScancodeInformation(packageUrl); - if (componentScancodeInfos == null) { - return null; - } - addOriginData(packageUrl, componentScancodeInfos); - applyCurations(packageUrl, componentScancodeInfos); - - return componentScancodeInfos; - - } else { - return null; - } - - } - - private boolean isFeatureActive() { - - if (!this.featureLogged) { - if (this.featureFlag) { - LOG.warn(LogMessages.SCANCODE_PROCESSOR_STARTING.msg()); - } else { - LOG.info(LogMessages.SCANCODE_FEATURE_DEACTIVATED.msg()); - } - this.featureLogged = true; - } - return this.featureFlag; - } - - /** - * Read scancode information for the given package from local file storage. - * - * @param packageUrl The package url of the package - * @return the read scancode information, null if no information was found - * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no - * data available no exception will be thrown. Instead null will be return in such a case. - */ - private ScancodeComponentInfo determineScancodeInformation(String packageUrl) throws ComponentInfoAdapterException { - - ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore, - this.minLicensefileNumberOfLines); - String packagePathPart = this.packageURLHandler.pathFor(packageUrl); - String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json"; - - File scanCodeFile = new File(path); - if (!scanCodeFile.exists()) { - LOG.debug("No Scancode info available for PackageURL '{}'", packageUrl); - return null; - } - LOG.debug("Found Scancode info for PackageURL '{}'", packageUrl); - try (InputStream is = new FileInputStream(scanCodeFile)) { - - JsonNode scancodeJson = mapper.readTree(is); - - for (JsonNode file : scancodeJson.get("files")) { - if ("directory".equals(file.get("type").asText())) { - continue; - } - if (file.get("path").asText().contains("/NOTICE")) { - componentScancodeInfos.addNoticeFilePath("file:" + this.repoBasePath + "/" - + this.packageURLHandler.pathFor(packageUrl) + "/" + file.get("path").asText(), 100.0); - } - double licenseTextRatio = file.get("percentage_of_license_text").asDouble(); - boolean takeCompleteFile = licenseTextRatio >= this.licenseToTextRatioToTakeCompleteFile; - for (JsonNode cr : file.get("copyrights")) { - if (cr.has("copyright")) { - componentScancodeInfos.addCopyright(cr.get("copyright").asText()); - } else { - componentScancodeInfos.addCopyright(cr.get("value").asText()); - } - } - - // special handling for Classpath-exception-2.0 - Map spdxIdMap = new HashMap<>(); - boolean classPathExceptionExists = false; - int numberOfGplLicenses = 0; - for (JsonNode li : file.get("licenses")) { - String licenseName = li.get("spdx_license_key").asText(); - if ("Classpath-exception-2.0".equals(licenseName)) { - classPathExceptionExists = true; - } - if (!spdxIdMap.containsKey(licenseName)) { - spdxIdMap.put(licenseName, licenseName); - if (licenseName.startsWith("GPL")) { - numberOfGplLicenses++; - } - } - } - if (classPathExceptionExists) { - if (numberOfGplLicenses == 0) { - LOG.warn(LogMessages.CLASSPATHEXCEPTION_WITHOUT_GPL.msg(), packageUrl); - } else if (numberOfGplLicenses > 1) { - LOG.warn(LogMessages.CLASSPATHEXCEPTION_MULTIPLE_GPL.msg(), packageUrl); - } else { - LOG.debug("Adjusting GPL license to contain WITH Classpath-execption-2.0 for " + packageUrl); - for (String licenseName : spdxIdMap.keySet()) { - if (licenseName.startsWith("GPL")) { - spdxIdMap.put(licenseName, licenseName + " WITH Classpath-exception-2.0"); - } - } - // do not output the Classpath-exception-2.0 as separate License - spdxIdMap.remove("Classpath-exception-2.0"); - } - } - for (JsonNode li : file.get("licenses")) { - String licenseid = li.get("key").asText(); - String licenseName = li.get("spdx_license_key").asText(); - String effectiveLicenseName = spdxIdMap.get(licenseName); - if (effectiveLicenseName == null) { - // not contained in map --> this must be the Classpath-exception-2.0 - continue; - } else { - licenseName = effectiveLicenseName; - if (licenseName.endsWith("WITH Classpath-exception-2.0")) { - licenseid = licenseid + "WITH Classpath-exception-2.0"; - } - } - String licenseDefaultUrl = li.get("scancode_text_url").asText(); - licenseDefaultUrl = normalizeLicenseUrl(packageUrl, licenseDefaultUrl); - double score = li.get("score").asDouble(); - String licenseFilePath = file.get("path").asText(); - int startLine = li.get("start_line").asInt(); - int endLine = li.get("end_line").asInt(); - if (!takeCompleteFile) { - licenseFilePath += "#L" + startLine; - if (endLine != startLine) { - licenseFilePath += "-L" + endLine; - } - } - - licenseFilePath = normalizeLicenseUrl(packageUrl, licenseFilePath); - String givenLicenseText = null; - if (licenseFilePath != null && licenseFilePath.startsWith("file:")) { - givenLicenseText = this.contentProvider.getContentForUri(licenseFilePath).getContent(); - } - - componentScancodeInfos.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseFilePath, - givenLicenseText, endLine - startLine); - } - } - // if (componentScancodeInfos.getLicenses().size() == 0) { - // componentScancodeInfos.addLicense("unknown", "unknown", "", 100.0, "", "", 0); - // } - if (componentScancodeInfos.getNoticeFilePath() != null - && componentScancodeInfos.getNoticeFilePath().startsWith("file:")) { - componentScancodeInfos.setNoticeFileContent( - this.contentProvider.getContentForUri(componentScancodeInfos.getNoticeFilePath()).getContent()); - } - LOG.debug("Scancode info for package {}: {} license, {} copyrights, {} NOTICE files", packageUrl, - componentScancodeInfos.getLicenses().size(), componentScancodeInfos.getCopyrights().size(), - componentScancodeInfos.getNoticeFilePath() != null ? 1 : 0); - - } catch (IOException e) { - throw new ComponentInfoAdapterException("Could not read Scancode JSON", e); - } - return componentScancodeInfos; - } - - /** - * Adds the data about the origin of the package which is (optionally) contained in file "origin.yaml" - * - * @param packageUrl the identifier of the package - * @param componentScancodeInfos the componentScancodeInfos to add the origin data to - * @throws ComponentInfoAdapterException if there was an error when reading the file - */ - private void addOriginData(String packageUrl, ScancodeComponentInfo componentScancodeInfos) - throws ComponentInfoAdapterException { - - String packagePathPart = this.packageURLHandler.pathFor(packageUrl); - String path = this.repoBasePath + "/" + packagePathPart + "/origin.yaml"; - - File originFile = new File(path); - if (!originFile.exists()) { - LOG.debug("No origin info available for PackageURL '{}'", packageUrl); - return; - } - LOG.debug("Found origin info for PackageURL '{}'", packageUrl); - - try (InputStream is = new FileInputStream(originFile)) { - - JsonNode originYaml = yamlMapper.readTree(is); - - String sourceDownloadUrl = originYaml.get("sourceDownloadUrl") != null - ? originYaml.get("sourceDownloadUrl").asText() - : null; - String packageDownloadUrl = originYaml.get("packageDownloadUrl") != null - ? originYaml.get("packageDownloadUrl").asText() - : null; - String note = originYaml.get("note") != null ? originYaml.get("note").asText() : null; - if (note != null) { - LOG.debug("Note: " + note); - } - - componentScancodeInfos.setSourceDownloadUrl(sourceDownloadUrl); - componentScancodeInfos.setPackageDownloadUrl(packageDownloadUrl); - - } catch (IOException e) { - throw new ComponentInfoAdapterException("Could not read origin.yaml", e); - } - - } - - /** - * Checks for the existence of curations for the given package in the local file system and applies them to the - * component scancode infos. - * - * @param packageUrl the identifier of the package - * @param componentScancodeInfos the componentScancodeInfos to curate - * @throws ComponentInfoAdapterException if the curations could not be read - */ - private void applyCurations(String packageUrl, ScancodeComponentInfo componentScancodeInfos) - throws ComponentInfoAdapterException { - - String packagePathPart = this.packageURLHandler.pathFor(packageUrl); - - File curationsFile = new File(this.curationsFileName); - if (!curationsFile.exists()) { - if (!this.curationsExistenceLogged) { - // log only once - this.curationsExistenceLogged = true; - LOG.info(LogMessages.CURATIONS_NOT_EXISTING.msg(), this.curationsFileName); - } - } else { - if (!this.curationsExistenceLogged) { - // log only once - this.curationsExistenceLogged = true; - LOG.info(LogMessages.CURATIONS_PROCESSING.msg(), this.curationsFileName); - } - try (InputStream isc = new FileInputStream(this.curationsFileName)) { - - JsonNode curationsObj = yamlMapper.readTree(isc); - - for (JsonNode curations : curationsObj.get("artifacts")) { - String component = curations.get("name").asText(); - if (component.equals(packagePathPart)) { - ScancodeComponentInfo oneComponent = componentScancodeInfos; - if (curations.get("copyrights") != null) { - oneComponent.clearCopyrights(); - int authorCount = curations.get("copyrights").size(); - for (int i = 0; i < authorCount; i++) { - oneComponent.addCopyright(curations.get("copyrights").get(i).asText()); - } - } - if (curations.get("url") != null) { - String url = curations.get("url").asText(); - oneComponent.setSourceRepoUrl(url); - } - if (curations.get("licenses") != null) { - oneComponent.clearLicenses(); - for (JsonNode licenseNode : curations.get("licenses")) { - String license = licenseNode.get("license").asText(); - String url = licenseNode.get("url").asText(); - String givenLicenseText = null; - if (url != null && url.startsWith("file:")) { - givenLicenseText = this.contentProvider.getContentForUri(url).getContent(); - } - - oneComponent.addLicense(license, license, "", 110, url, givenLicenseText, Integer.MAX_VALUE); - } - } - } - } - - } catch (IOException e) { - throw new ComponentInfoAdapterException("Could not read Curations JSON", e); - } - - } - - } - - /** - * Adjustment of license paths/urls so that they might retrieved - * - * @param packageUrl package url of the package - * @param licenseFilePath the original path or URL - * @return the adjusted path or url as a url - */ - private String normalizeLicenseUrl(String packageUrl, String licenseFilePath) { - - String adjustedLicenseFilePath; - if (licenseFilePath != null) { - if (licenseFilePath.startsWith("http")) { - // TODO - adjustedLicenseFilePath = licenseFilePath.replace( - "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses", - "https://scancode-licensedb.aboutcode.org"); - adjustedLicenseFilePath = adjustedLicenseFilePath.replace("github.com", "raw.github.com").replace("/tree", ""); - } else { - adjustedLicenseFilePath = "file:" + this.repoBasePath + "/" + this.packageURLHandler.pathFor(packageUrl) + "/" - + licenseFilePath; - LOG.debug("LOCAL LICENSE: " + licenseFilePath); - } - } else { - adjustedLicenseFilePath = null; - // licenseFilePath = null;// "??????";// defaultGithubLicenseURL(repo); - LOG.debug("NONLOCAL LICENSE: {} (was: {})" + adjustedLicenseFilePath, licenseFilePath); - } - return adjustedLicenseFilePath; - } - -} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfo.java new file mode 100644 index 00000000..82d4a306 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfo.java @@ -0,0 +1,22 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +/** + * Contains raw scancode data and any additional data needed to create a ComponentInfo structure. + * + */ +public class ScancodeRawComponentInfo { + + public String rawScancodeResult; + + public String sourceDownloadUrl; + + public String packageDownloadUrl; + + /** + * The constructor. + */ + public ScancodeRawComponentInfo() { + + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoPovider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoPovider.java new file mode 100644 index 00000000..3bfc8ad6 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoPovider.java @@ -0,0 +1,44 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import com.devonfw.tools.solicitor.componentinfo.ComponentContentProvider; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; + +/** + * Provider for {@link ScancodeRawComponentInfo} + * + */ +public interface ScancodeRawComponentInfoPovider extends ComponentContentProvider { + + /** + * Retrieve the {@link ScancodeRawComponentInfo} for the package given by its PackageURL. + * + * @param packageUrl the identifier for the package + * @return the raw data based on scancode and supplemental data. null if no data is available. + * @throws ComponentInfoAdapterException is something unexpected happens + */ + ScancodeRawComponentInfo readScancodeData(String packageUrl) throws ComponentInfoAdapterException; + + /** + * Creates a pkgcontent-URI (see {@link ComponentContentProvider}) from the relative local file path. + * + * @param packageUrl the PackageUrl of the component. Implementations might use this reference to the component to + * check within the components stored data how the requested uri should be built. + * @param path the path referencing file content + * + * @return a pkgContent URI which might be used for retrieving the content vis + * {@link ComponentContentProvider#retrieveContent(String, String)} + */ + String pkgContentUriFromPath(String packageUrl, String path); + + /** + * Checks if the argument seems to be a (relative) path pointing to some content within the package. + * + * @param packageUrl the PackageUrl of the component. Implementations might use this reference to the component to + * check within the components stored data if the given path is valid. + * @param path the path to check + * + * @return true if the seems to be a correct path, false otherwise. + */ + boolean isLocalContentPath(String packageUrl, String path); + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java new file mode 100644 index 00000000..0c3a9984 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java @@ -0,0 +1,268 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.curation.UncuratedComponentInfoProvider; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.github.packageurl.PackageURL; + +/** + * {@link UncuratedComponentInfoProvider} which delivers data based on scancode data. + * + */ +@Component +public class UncuratedScancodeComponentInfoProvider implements UncuratedComponentInfoProvider { + + private static final Logger LOG = LoggerFactory.getLogger(UncuratedScancodeComponentInfoProvider.class); + + private static final ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + + private String repoBasePath; + + private double minLicenseScore; + + private int minLicensefileNumberOfLines; + + private double licenseToTextRatioToTakeCompleteFile = 90; + + private AllKindsPackageURLHandler packageURLHandler; + + private ScancodeRawComponentInfoPovider fileScancodeRawComponentInfoProvider; + + /** + * The constructor. + * + * @param fileScancodeRawComponentInfoProvider the provide for the raw scancode data + * @param packageURLHandler the handler for dealing with {@link PackageURL}s. + */ + @Autowired + public UncuratedScancodeComponentInfoProvider(ScancodeRawComponentInfoPovider fileScancodeRawComponentInfoProvider, + AllKindsPackageURLHandler packageURLHandler) { + + this.fileScancodeRawComponentInfoProvider = fileScancodeRawComponentInfoProvider; + this.packageURLHandler = packageURLHandler; + + } + + /** + * Sets repoBasePath. + * + * @param repoBasePath new value of repoBasePath. + */ + @Value("${solicitor.scancode.repo-base-path}") + public void setRepoBasePath(String repoBasePath) { + + this.repoBasePath = repoBasePath; + } + + /** + * Sets minLicenseScore. + * + * @param minLicenseScore new value of minLicenseScore. + */ + @Value("${solicitor.scancode.min-license-score}") + public void setMinLicenseScore(double minLicenseScore) { + + this.minLicenseScore = minLicenseScore; + } + + /** + * Sets minLicensefileNumberOfLines. + * + * @param minLicensefileNumberOfLines new value of minLicensefileNumberOfLines. + */ + @Value("${solicitor.scancode.min-licensefile-number-of-lines}") + public void setMinLicensefileNumberOfLines(int minLicensefileNumberOfLines) { + + this.minLicensefileNumberOfLines = minLicensefileNumberOfLines; + } + + /** + * Read scancode information for the given package from local file storage. + * + * @param packageUrl The package url of the package + * @return the read scancode information, null if no information was found + * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no + * data available no exception will be thrown. Instead null will be return in such a case. + */ + @Override + public ScancodeComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException { + + ScancodeRawComponentInfo rawScancodeData = this.fileScancodeRawComponentInfoProvider.readScancodeData(packageUrl); + if (rawScancodeData == null) { + return null; + } + + ScancodeComponentInfo componentScancodeInfos = parseAndMapScancodeJson(packageUrl, rawScancodeData); + addSupplementedData(rawScancodeData, componentScancodeInfos); + LOG.debug("Scancode info for package {}: {} license, {} copyrights, {} NOTICE files", packageUrl, + componentScancodeInfos.getLicenses().size(), componentScancodeInfos.getCopyrights().size(), + componentScancodeInfos.getNoticeFileUrl() != null ? 1 : 0); + + return componentScancodeInfos; + } + + /** + * @param rawScancodeData + * @param componentScancodeInfos + */ + private void addSupplementedData(ScancodeRawComponentInfo rawScancodeData, + ScancodeComponentInfo componentScancodeInfos) { + + componentScancodeInfos.setSourceDownloadUrl(rawScancodeData.sourceDownloadUrl); + componentScancodeInfos.setPackageDownloadUrl(rawScancodeData.packageDownloadUrl); + } + + /** + * @param packageUrl + * @param rawScancodeData + * @return + * @throws ComponentInfoAdapterException + */ + private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, ScancodeRawComponentInfo rawScancodeData) + throws ComponentInfoAdapterException { + + ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore, + this.minLicensefileNumberOfLines); + componentScancodeInfos.setPackageUrl(packageUrl); + + JsonNode scancodeJson; + try { + scancodeJson = mapper.readTree(rawScancodeData.rawScancodeResult); + } catch (JsonProcessingException e) { + throw new ComponentInfoAdapterException("Could not parse Scancode JSON", e); + } + + for (JsonNode file : scancodeJson.get("files")) { + if ("directory".equals(file.get("type").asText())) { + continue; + } + if (file.get("path").asText().contains("/NOTICE")) { + componentScancodeInfos.addNoticeFileUrl( + this.fileScancodeRawComponentInfoProvider.pkgContentUriFromPath(packageUrl, file.get("path").asText()), + 100.0); + } + double licenseTextRatio = file.get("percentage_of_license_text").asDouble(); + boolean takeCompleteFile = licenseTextRatio >= this.licenseToTextRatioToTakeCompleteFile; + for (JsonNode cr : file.get("copyrights")) { + if (cr.has("copyright")) { + componentScancodeInfos.addCopyright(cr.get("copyright").asText()); + } else { + componentScancodeInfos.addCopyright(cr.get("value").asText()); + } + } + + // special handling for Classpath-exception-2.0 + Map spdxIdMap = new HashMap<>(); + boolean classPathExceptionExists = false; + int numberOfGplLicenses = 0; + for (JsonNode li : file.get("licenses")) { + String licenseName = li.get("spdx_license_key").asText(); + if ("Classpath-exception-2.0".equals(licenseName)) { + classPathExceptionExists = true; + } + if (!spdxIdMap.containsKey(licenseName)) { + spdxIdMap.put(licenseName, licenseName); + if (licenseName.startsWith("GPL")) { + numberOfGplLicenses++; + } + } + } + if (classPathExceptionExists) { + if (numberOfGplLicenses == 0) { + LOG.warn(LogMessages.CLASSPATHEXCEPTION_WITHOUT_GPL.msg(), packageUrl); + } else if (numberOfGplLicenses > 1) { + LOG.warn(LogMessages.CLASSPATHEXCEPTION_MULTIPLE_GPL.msg(), packageUrl); + } else { + LOG.debug("Adjusting GPL license to contain WITH Classpath-execption-2.0 for " + packageUrl); + for (String licenseName : spdxIdMap.keySet()) { + if (licenseName.startsWith("GPL")) { + spdxIdMap.put(licenseName, licenseName + " WITH Classpath-exception-2.0"); + } + } + // do not output the Classpath-exception-2.0 as separate License + spdxIdMap.remove("Classpath-exception-2.0"); + } + } + for (JsonNode li : file.get("licenses")) { + String licenseid = li.get("key").asText(); + String licenseName = li.get("spdx_license_key").asText(); + String effectiveLicenseName = spdxIdMap.get(licenseName); + if (effectiveLicenseName == null) { + // not contained in map --> this must be the Classpath-exception-2.0 + continue; + } else { + licenseName = effectiveLicenseName; + if (licenseName.endsWith("WITH Classpath-exception-2.0")) { + licenseid = licenseid + "WITH Classpath-exception-2.0"; + } + } + String licenseDefaultUrl = li.get("scancode_text_url").asText(); + licenseDefaultUrl = normalizeLicenseUrl(packageUrl, licenseDefaultUrl); + double score = li.get("score").asDouble(); + String licenseUrl = file.get("path").asText(); + int startLine = li.get("start_line").asInt(); + int endLine = li.get("end_line").asInt(); + if (!takeCompleteFile) { + licenseUrl += "#L" + startLine; + if (endLine != startLine) { + licenseUrl += "-L" + endLine; + } + } + + licenseUrl = normalizeLicenseUrl(packageUrl, licenseUrl); + String givenLicenseText = null; + if (licenseUrl != null) { + givenLicenseText = this.fileScancodeRawComponentInfoProvider.retrieveContent(packageUrl, licenseUrl); + } + + componentScancodeInfos.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseUrl, + givenLicenseText, endLine - startLine); + } + } + if (componentScancodeInfos.getNoticeFileUrl() != null) { + componentScancodeInfos.setNoticeFileContent(this.fileScancodeRawComponentInfoProvider.retrieveContent(packageUrl, + componentScancodeInfos.getNoticeFileUrl())); + } + return componentScancodeInfos; + } + + /** + * Adjustment of license paths/urls so that they might retrieved + * + * @param packageUrl package url of the package + * @param licenseUrl the original path or URL + * @return the adjusted path or url as a url + */ + private String normalizeLicenseUrl(String packageUrl, String licenseUrl) { + + String adjustedLicenseUrl = licenseUrl; + if (licenseUrl != null) { + if (licenseUrl.startsWith("http")) { + adjustedLicenseUrl = licenseUrl.replace( + "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses", + "https://scancode-licensedb.aboutcode.org"); + adjustedLicenseUrl = adjustedLicenseUrl.replace("github.com", "raw.github.com").replace("/tree", ""); + } else if (this.fileScancodeRawComponentInfoProvider.isLocalContentPath(packageUrl, licenseUrl)) { + adjustedLicenseUrl = this.fileScancodeRawComponentInfoProvider.pkgContentUriFromPath(packageUrl, + licenseUrl); + LOG.debug("LOCAL LICENSE: " + licenseUrl); + } + } + return adjustedLicenseUrl; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/package-info.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/package-info.java new file mode 100644 index 00000000..5c669680 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/package-info.java @@ -0,0 +1,8 @@ +/** + * Interfaces and classes related to creating {@link com.devonfw.tools.solicitor.componentinfo.ComponentInfo} from data + * obtained by doing deep license scans with ScanCode. + *

        + * See {@link com.devonfw.tools.solicitor.componentinfo} for an overview diagram of interfaces/classes. + *

        + */ +package com.devonfw.tools.solicitor.componentinfo.scancode; \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/package-info.java b/core/src/main/java/com/devonfw/tools/solicitor/package-info.java index 580ecea4..12188f6a 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/package-info.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/package-info.java @@ -1,10 +1,16 @@ /** *

        * Root package of the Solicitor application. - *

        *

        - * The following diagram shows the Spring Beans of Solicitor and their major interconnections: - *

        + * The following diagram shows the main Spring Beans of Solicitor and their major interconnections: + *

        * Solicitor Spring Beans + *

        + * The interfaces / classes / spring beans of the componentinfo package and subpackages is shown in the following + * diagram: + *

        + * Interfaces / classes and Spring Beans of the
+ * componentinfo packages + *

        */ package com.devonfw.tools.solicitor; diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/CurationReadingTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/CurationReadingTest.java new file mode 100644 index 00000000..f7841fd8 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/CurationReadingTest.java @@ -0,0 +1,105 @@ +package com.devonfw.tools.solicitor.componentinfo.curating; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.File; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.CurationList; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +/** + * This class contains JUnit test methods for deserializing YAML encoded curation data. + */ +class CurationReadingTest { + + /** + * Test reading a curation for a component which has all fields set. + * + * @throws IOException might be thown by the ObjectMapper. + * + */ + @Test + public void testReadSingleCurationComplete() throws IOException { + + // given + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + mapper.findAndRegisterModules(); + + // when + ComponentInfoCuration curation = mapper.readValue( + new File("src/test/resources/curations/curation_with_all_fields_set.yaml"), ComponentInfoCuration.class); + + // then + assertNotNull(curation); + assertEquals("pkg/maven/somenamespace/somecomponent/2.3.4", curation.getName()); + assertEquals("some note on the curation", curation.getNote()); + assertEquals("http://the/sourceRepoUrl", curation.getUrl()); + assertEquals(3, curation.getLicenses().size()); + assertEquals("Apache-2.0", curation.getLicenses().get(0).getLicense()); + assertEquals("https://scancode-licensedb.aboutcode.org/apache-2.0.LICENSE", curation.getLicenses().get(0).getUrl()); + assertEquals(2, curation.getCopyrights().size()); + assertEquals("Copyright (c) 2003 First Holder", curation.getCopyrights().get(0)); + + } + + /** + * Test reading a curation for a component where some fields are not set. + * + * @throws IOException might be thown by the ObjectMapper. + * + */ + @Test + public void testReadSingleCurationPartial() throws IOException { + + // given + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + mapper.findAndRegisterModules(); + + // when + ComponentInfoCuration curation = mapper.readValue( + new File("src/test/resources/curations/curation_with_fields_missing.yaml"), ComponentInfoCuration.class); + + // then + assertNotNull(curation); + assertEquals("pkg/maven/somenamespace/somecomponent/2.3.4", curation.getName()); + assertEquals("some note on the curation", curation.getNote()); + assertNull(curation.getUrl()); + assertNull(curation.getLicenses()); + assertEquals(0, curation.getCopyrights().size()); + + } + + /** + * Test reading an aarry of curation infos. + * + * @throws IOException might be thown by the ObjectMapper. + * + */ + @Test + public void testReadListOfCurations() throws IOException { + + // given + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + mapper.findAndRegisterModules(); + + // when + CurationList curations = mapper.readValue(new File("src/test/resources/curations/array_of_curations.yaml"), + CurationList.class); + + // then + assertNotNull(curations); + assertEquals(2, curations.getArtifacts().size()); + assertEquals("pkg/maven/somenamespace/somecomponent/2.3.4", curations.getArtifacts().get(0).getName()); + assertEquals("Apache-2.0", curations.getArtifacts().get(0).getLicenses().get(0).getLicense()); + assertEquals("pkg/maven/somenamespace/somecomponent/2.3.5", curations.getArtifacts().get(1).getName()); + assertEquals("BSD-2-Clause", curations.getArtifacts().get(1).getLicenses().get(0).getLicense()); + + } +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java new file mode 100644 index 00000000..b08f0c85 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java @@ -0,0 +1,175 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.LicenseInfo; +import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCurator; +import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCuratorImpl; +import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; + +/** + * This class contains JUnit test methods for the {@link ScancodeComponentInfoAdapter} class. + */ +class ScancodeComponentInfoAdapterTest { + + // the object under test + ScancodeComponentInfoAdapter scancodeComponentInfoAdapter; + + UncuratedScancodeComponentInfoProvider uncuratedScancodeComponentInfoProvider; + + FileScancodeRawComponentInfoProvider fileScancodeRawComponentInfoProvider; + + ComponentInfoCurator componentInfoCuratorImpl; + + SingleFileCurationProvider singleFileCurationProvider; + + @BeforeEach + public void setup() { + + AllKindsPackageURLHandler packageURLHandler = Mockito.mock(AllKindsPackageURLHandler.class); + + Mockito.when(packageURLHandler.pathFor("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0")) + .thenReturn("pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0"); + DirectUrlWebContentProvider contentProvider = new DirectUrlWebContentProvider(false); + + this.fileScancodeRawComponentInfoProvider = new FileScancodeRawComponentInfoProvider(contentProvider, + packageURLHandler); + this.fileScancodeRawComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); + + this.uncuratedScancodeComponentInfoProvider = new UncuratedScancodeComponentInfoProvider( + this.fileScancodeRawComponentInfoProvider, packageURLHandler); + this.uncuratedScancodeComponentInfoProvider.setMinLicensefileNumberOfLines(5); + this.uncuratedScancodeComponentInfoProvider.setMinLicenseScore(90.0); + this.uncuratedScancodeComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); + + this.singleFileCurationProvider = new SingleFileCurationProvider(packageURLHandler); + this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/curations.yaml"); + + this.componentInfoCuratorImpl = new ComponentInfoCuratorImpl(this.singleFileCurationProvider, + this.fileScancodeRawComponentInfoProvider); + + this.scancodeComponentInfoAdapter = new ScancodeComponentInfoAdapter(this.uncuratedScancodeComponentInfoProvider, + this.componentInfoCuratorImpl); + this.scancodeComponentInfoAdapter.setFeatureFlag(true); + + } + + /** + * Test the {@link ScancodeFileAdapter#getComponentInfo(String)} method when such package is known. + * + * @throws ComponentInfoAdapterException + */ + @Test + public void testGetComponentInfoNaPackage() throws ComponentInfoAdapterException { + + // given + + // when + ComponentInfo componentInfo = this.scancodeComponentInfoAdapter + .getComponentInfo("pkg:maven/com.devonfw.tools/unknown@0.1.0"); + + // then + assertNull(componentInfo); + } + + /** + * Test the {@link ScancodeFileAdapter#getComponentInfo(String)} method when no curations are available. + * + * @throws ComponentInfoAdapterException + */ + @Test + public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterException { + + // given + this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/nonexisting.yaml"); + + // when + ComponentInfo componentInfo = this.scancodeComponentInfoAdapter + .getComponentInfo("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0"); + + // then + assertNotNull(componentInfo); + assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", componentInfo.getPackageUrl()); + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", + componentInfo.getNoticeFileContent()); + assertEquals("pkgcontent:/NOTICE.txt", componentInfo.getNoticeFileUrl()); + assertEquals(1, componentInfo.getCopyrights().size()); + assertEquals("Copyright 2023 devonfw", componentInfo.getCopyrights().toArray()[0]); + assertEquals(2, componentInfo.getLicenses().size()); + + boolean apacheFound = false; + boolean unknownFound = false; + for (LicenseInfo li : componentInfo.getLicenses()) { + if (li.getSpdxid().equals("Apache-2.0")) { + Assertions.assertTrue(li.getGivenLicenseText().contains("Unless required by applicable")); + apacheFound = true; + } + if (li.getSpdxid().equals("LicenseRef-scancode-unknown-license-reference")) { + Assertions.assertNull(li.getGivenLicenseText()); + unknownFound = true; + } + } + assertTrue(apacheFound && unknownFound); + + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", + componentInfo.getNoticeFileContent()); + assertEquals("https://somehost/test-project-for-deep-license-scan-0.1.0.jar", + componentInfo.getPackageDownloadUrl()); + assertEquals("https://somehost/test-project-for-deep-license-scan-0.1.0-sources.jar", + componentInfo.getSourceDownloadUrl()); + + } + + /** + * Test the {@link ScancodeFileAdapter#getComponentInfo(String)} method when curations are existing. + * + * @throws ComponentInfoAdapterException + */ + @Test + public void testGetComponentInfoWithCurations() throws ComponentInfoAdapterException { + + // when + ComponentInfo componentInfo = this.scancodeComponentInfoAdapter + .getComponentInfo("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0"); + + // then + assertNotNull(componentInfo); + assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", componentInfo.getPackageUrl()); + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", + componentInfo.getNoticeFileContent()); + assertEquals("pkgcontent:/NOTICE.txt", componentInfo.getNoticeFileUrl()); + assertEquals(1, componentInfo.getCopyrights().size()); + assertEquals("Copyright (c) 2023 somebody", componentInfo.getCopyrights().toArray()[0]); + assertEquals(1, componentInfo.getLicenses().size()); + + boolean mitFound = false; + for (LicenseInfo li : componentInfo.getLicenses()) { + if (li.getSpdxid().equals("MIT")) { + Assertions.assertEquals("https://some/license/url", li.getLicenseUrl()); + mitFound = true; + } + } + assertTrue(mitFound); + + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", + componentInfo.getNoticeFileContent()); + assertEquals("https://somehost/test-project-for-deep-license-scan-0.1.0.jar", + componentInfo.getPackageDownloadUrl()); + assertEquals("https://somehost/test-project-for-deep-license-scan-0.1.0-sources.jar", + componentInfo.getSourceDownloadUrl()); + assertEquals("http://some/url", componentInfo.getSourceRepoUrl()); + } + +} diff --git a/core/src/test/resources/curations/array_of_curations.yaml b/core/src/test/resources/curations/array_of_curations.yaml new file mode 100644 index 00000000..5ff9e40a --- /dev/null +++ b/core/src/test/resources/curations/array_of_curations.yaml @@ -0,0 +1,13 @@ +artifacts: +- name: "pkg/maven/somenamespace/somecomponent/2.3.4" + note: "some note on the curation" + licenses: + - license: "Apache-2.0" + url: "https://scancode-licensedb.aboutcode.org/apache-2.0.LICENSE" + copyrights: [] +- name: "pkg/maven/somenamespace/somecomponent/2.3.5" + note: "some note on the curation" + licenses: + - license: "BSD-2-Clause" + url: "https://scancode-licensedb.aboutcode.org/bsd-simplified.LICENSE" + copyrights: [] diff --git a/core/src/test/resources/curations/curation_with_all_fields_set.yaml b/core/src/test/resources/curations/curation_with_all_fields_set.yaml new file mode 100644 index 00000000..f1a6cd6d --- /dev/null +++ b/core/src/test/resources/curations/curation_with_all_fields_set.yaml @@ -0,0 +1,13 @@ +name: "pkg/maven/somenamespace/somecomponent/2.3.4" +note: "some note on the curation" +url: "http://the/sourceRepoUrl" +licenses: +- license: "Apache-2.0" + url: "https://scancode-licensedb.aboutcode.org/apache-2.0.LICENSE" +- license: "BSD-2-Clause" + url: "https://scancode-licensedb.aboutcode.org/bsd-simplified.LICENSE" +- license: "CC-BY-3.0" + url: "https://scancode-licensedb.aboutcode.org/cc-by-3.0.LICENSE" +copyrights: +- "Copyright (c) 2003 First Holder" +- "Copyright (c) 2004 Second Holder" diff --git a/core/src/test/resources/curations/curation_with_fields_missing.yaml b/core/src/test/resources/curations/curation_with_fields_missing.yaml new file mode 100644 index 00000000..57e4f2de --- /dev/null +++ b/core/src/test/resources/curations/curation_with_fields_missing.yaml @@ -0,0 +1,14 @@ +name: "pkg/maven/somenamespace/somecomponent/2.3.4" +note: "some note on the curation" +#url: "http://the/sourceRepoUrl" +#licenses: +#- license: "Apache-2.0" +# url: "https://scancode-licensedb.aboutcode.org/apache-2.0.LICENSE" +#- license: "BSD-2-Clause" +# url: "https://scancode-licensedb.aboutcode.org/bsd-simplified.LICENSE" +#- license: "CC-BY-3.0" +# url: "https://scancode-licensedb.aboutcode.org/cc-by-3.0.LICENSE" +copyrights: [] +# the copyright list exists but is empty! +#- "Copyright (c) 2003 First Holder" +#- "Copyright (c) 2004 Second Holder" diff --git a/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/origin.yaml b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/origin.yaml new file mode 100644 index 00000000..66522f1f --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/origin.yaml @@ -0,0 +1,5 @@ +# This file contains metadata about the orgin of the package and the sources. +# This file was automatically created but might manually be edited if the contained data is not correct +sourceDownloadUrl: https://somehost/test-project-for-deep-license-scan-0.1.0-sources.jar +packageDownloadUrl: https://somehost/test-project-for-deep-license-scan-0.1.0.jar +# note: to add comments: write them here and remove the hash at the beginning of the line diff --git a/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancode.json b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancode.json new file mode 100644 index 00000000..ea167971 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancode.json @@ -0,0 +1,252 @@ +{ + "headers": [ + { + "tool_name": "scancode-toolkit", + "tool_version": "31.0.2", + "options": { + "input": [ + "/somepath/Source/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources" + ], + "--consolidate": true, + "--copyright": true, + "--json-pp": "/somepath/Source/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancode.json", + "--license": true, + "--license-text": true, + "--only-findings": true, + "--processes": "16" + }, + "notice": "Generated with ScanCode and provided on an \"AS IS\" BASIS, WITHOUT WARRANTIES\nOR CONDITIONS OF ANY KIND, either express or implied. No content created from\nScanCode should be considered or used as legal advice. Consult an Attorney\nfor any legal advice.\nScanCode is a free software code scanning tool from nexB Inc. and others.\nVisit https://github.com/nexB/scancode-toolkit/ for support and download.", + "start_timestamp": "2023-08-10T143029.366986", + "end_timestamp": "2023-08-10T143032.887088", + "output_format_version": "2.0.0", + "duration": 3.520116090774536, + "message": null, + "errors": [], + "warnings": [ + "The --consolidate option will be deprecated in a future version of scancode-toolkit." + ], + "extra_data": { + "system_environment": { + "operating_system": "linux", + "cpu_architecture": "64", + "platform": "Linux-5.10.102.1-microsoft-standard-WSL2-x86_64-with-glibc2.29", + "platform_version": "#1 SMP Wed Mar 2 00:30:59 UTC 2022", + "python_version": "3.8.10 (default, Jun 22 2022, 20:18:18) \n[GCC 9.4.0]" + }, + "spdx_license_list_version": "3.17", + "files_count": 3 + } + } + ], + "consolidated_components": [ + { + "type": "holders", + "identifier": "devonfw_1", + "consolidated_license_expression": "apache-2.0", + "consolidated_holders": [ + "devonfw" + ], + "consolidated_copyright": "Copyright (c) devonfw", + "core_license_expression": "apache-2.0", + "core_holders": [ + "devonfw" + ], + "other_license_expression": null, + "other_holders": [], + "files_count": 1 + } + ], + "consolidated_packages": [], + "files": [ + { + "path": "sources/NOTICE.txt", + "type": "file", + "licenses": [ + { + "key": "apache-2.0", + "score": 100.0, + "name": "Apache License 2.0", + "short_name": "Apache 2.0", + "category": "Permissive", + "is_exception": false, + "is_unknown": false, + "owner": "Apache Software Foundation", + "homepage_url": "http://www.apache.org/licenses/", + "text_url": "http://www.apache.org/licenses/LICENSE-2.0", + "reference_url": "https://scancode-licensedb.aboutcode.org/apache-2.0", + "scancode_text_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/apache-2.0.LICENSE", + "scancode_data_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/apache-2.0.yml", + "spdx_license_key": "Apache-2.0", + "spdx_url": "https://spdx.org/licenses/Apache-2.0", + "start_line": 1, + "end_line": 1, + "matched_rule": { + "identifier": "spdx_license_id_apache-2.0_for_apache-2.0.RULE", + "license_expression": "apache-2.0", + "licenses": [ + "apache-2.0" + ], + "referenced_filenames": [], + "is_license_text": false, + "is_license_notice": false, + "is_license_reference": true, + "is_license_tag": false, + "is_license_intro": false, + "has_unknown": false, + "matcher": "2-aho", + "rule_length": 3, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 100 + }, + "matched_text": "This is a dummy notice file for testing. Code is under Apache-2.0." + } + ], + "license_expressions": [ + "apache-2.0" + ], + "percentage_of_license_text": 23.08, + "copyrights": [], + "holders": [], + "authors": [], + "consolidated_to": [], + "scan_errors": [] + }, + { + "path": "sources/src/main/java/com/devonfw/tools/test", + "type": "directory", + "licenses": [], + "license_expressions": [], + "percentage_of_license_text": 0, + "copyrights": [], + "holders": [], + "authors": [], + "consolidated_to": [ + "devonfw_1" + ], + "scan_errors": [] + }, + { + "path": "sources/src/main/java/com/devonfw/tools/test/SampleClass1.java", + "type": "file", + "licenses": [ + { + "key": "apache-2.0", + "score": 100.0, + "name": "Apache License 2.0", + "short_name": "Apache 2.0", + "category": "Permissive", + "is_exception": false, + "is_unknown": false, + "owner": "Apache Software Foundation", + "homepage_url": "http://www.apache.org/licenses/", + "text_url": "http://www.apache.org/licenses/LICENSE-2.0", + "reference_url": "https://scancode-licensedb.aboutcode.org/apache-2.0", + "scancode_text_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/apache-2.0.LICENSE", + "scancode_data_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/apache-2.0.yml", + "spdx_license_key": "Apache-2.0", + "spdx_url": "https://spdx.org/licenses/Apache-2.0", + "start_line": 5, + "end_line": 15, + "matched_rule": { + "identifier": "apache-2.0_7.RULE", + "license_expression": "apache-2.0", + "licenses": [ + "apache-2.0" + ], + "referenced_filenames": [], + "is_license_text": false, + "is_license_notice": true, + "is_license_reference": false, + "is_license_tag": false, + "is_license_intro": false, + "has_unknown": false, + "matcher": "2-aho", + "rule_length": 85, + "matched_length": 85, + "match_coverage": 100.0, + "rule_relevance": 100 + }, + "matched_text": " * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License." + } + ], + "license_expressions": [ + "apache-2.0" + ], + "percentage_of_license_text": 89.47, + "copyrights": [ + { + "copyright": "Copyright 2023 devonfw", + "start_line": 3, + "end_line": 3 + } + ], + "holders": [ + { + "holder": "devonfw", + "start_line": 3, + "end_line": 3 + } + ], + "authors": [], + "consolidated_to": [ + "devonfw_1" + ], + "scan_errors": [] + }, + { + "path": "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java", + "type": "file", + "licenses": [ + { + "key": "unknown-license-reference", + "score": 100.0, + "name": "Unknown License file reference", + "short_name": "Unknown License reference", + "category": "Unstated License", + "is_exception": false, + "is_unknown": true, + "owner": "Unspecified", + "homepage_url": null, + "text_url": "", + "reference_url": "https://scancode-licensedb.aboutcode.org/unknown-license-reference", + "scancode_text_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/unknown-license-reference.LICENSE", + "scancode_data_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/unknown-license-reference.yml", + "spdx_license_key": "LicenseRef-scancode-unknown-license-reference", + "spdx_url": "https://github.com/nexB/scancode-toolkit/tree/develop/src/licensedcode/data/licenses/unknown-license-reference.LICENSE", + "start_line": 4, + "end_line": 4, + "matched_rule": { + "identifier": "license-intro_26.RULE", + "license_expression": "unknown-license-reference", + "licenses": [ + "unknown-license-reference" + ], + "referenced_filenames": [], + "is_license_text": false, + "is_license_notice": false, + "is_license_reference": false, + "is_license_tag": false, + "is_license_intro": true, + "has_unknown": true, + "matcher": "2-aho", + "rule_length": 3, + "matched_length": 3, + "match_coverage": 100.0, + "rule_relevance": 100 + }, + "matched_text": " * It is licensed under the same license as the rest of Solicitor." + } + ], + "license_expressions": [ + "unknown-license-reference" + ], + "percentage_of_license_text": 8.82, + "copyrights": [], + "holders": [], + "authors": [], + "consolidated_to": [], + "scan_errors": [] + } + ] +} \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancodeScan.completed b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancodeScan.completed new file mode 100644 index 00000000..e69de29b diff --git a/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources.completed b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources.completed new file mode 100644 index 00000000..e69de29b diff --git a/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/NOTICE.txt b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/NOTICE.txt new file mode 100644 index 00000000..e5ec7905 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/NOTICE.txt @@ -0,0 +1 @@ +This is a dummy notice file for testing. Code is under Apache-2.0. \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/pom.xml b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/pom.xml new file mode 100644 index 00000000..5d4c9818 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/pom.xml @@ -0,0 +1,16 @@ + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + + + + com.devonfw.tools + test-project-for-deep-license-scan + 0.1.0 + \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/src/main/java/com/devonfw/tools/test/SampleClass1.java b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/src/main/java/com/devonfw/tools/test/SampleClass1.java new file mode 100644 index 00000000..7aa9067d --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/src/main/java/com/devonfw/tools/test/SampleClass1.java @@ -0,0 +1,20 @@ +package com.devonfw.tools.test; +/* + * Copyright 2023 devonfw + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class SampleClass1 { + +} diff --git a/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/src/main/java/com/devonfw/tools/test/SampleClass2.java b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/src/main/java/com/devonfw/tools/test/SampleClass2.java new file mode 100644 index 00000000..271282d8 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/Source/repo/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources/src/main/java/com/devonfw/tools/test/SampleClass2.java @@ -0,0 +1,9 @@ +package com.devonfw.tools.test; +/* + * This file is part of the test data for deep license scan support in Solicitor.. + * It is licensed under the same license as the rest of Solicitor. + */ + +public class SampleClass2 { + +} diff --git a/core/src/test/resources/scancodefileadapter/curations.yaml b/core/src/test/resources/scancodefileadapter/curations.yaml new file mode 100644 index 00000000..0c255f4d --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/curations.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrights: + - "Copyright (c) 2023 somebody" + url: "http://some/url" + licenses: + - license: MIT + url: https://some/license/url + diff --git a/core/src/test/resources/scancodefileadapter/scancodeScan.sh b/core/src/test/resources/scancodefileadapter/scancodeScan.sh new file mode 100644 index 00000000..def6589b --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/scancodeScan.sh @@ -0,0 +1,24 @@ +scancodeoptions="--processes 16 --copyright --only-findings --license --license-text --consolidate" +beginning=$(pwd) +mkdir scancodeOutput +so=$(pwd)/scancodeOutput +echo {\"artifacts\": [ > scancodeOutput/scancodeOut.json +dir=$(pwd)/Source + + if [ ! -f $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancode.json ] || [ ! -f $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancodeScan.completed ] + then + rm $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancodeScan.completed + rm $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancode.json + if [ -d $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources ] && [ -f $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources.completed ] + then + scancode $scancodeoptions --json-pp $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancode.json $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/sources + touch $dir/pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0/scancodeScan.completed + fi + fi + +printf "END" >> $so/scancodeOut.json +grep -v ",END" $so/scancodeOut.json > $so/scancodeOutput.json +rm $so/scancodeOut.json +echo ]} >> $so/scancodeOutput.json +mv $so/scancodeOutput.json $beginning + diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 2fb929ea..c254f1de 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1614,6 +1614,9 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.13.0:: +* https://github.com/devonfw/solicitor/issues/191: Refactoring of the (experimental) Scancode integration (formerly class ScancodeFileAdapter). +* https://github.com/devonfw/solicitor/issues/192: References to files within the sources of a component (e.g. license or notice files) +are now returned from the Scancode adapter by a local path prefixed by `$PKG_ROOT$/`. This replaces the usage of `file:` urls in this case. The same syntax needs to be used within curation data if the url pointing to license texts within the component needs to be curated. Changes in 1.12.0:: * https://github.com/devonfw/solicitor/issues/182: Add new data quality and traceability attributes to the ComponentInfo data structure. From f294a3ea1acb3e03453ec31249807e5d489cc4e6 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 18 Aug 2023 08:58:56 +0200 Subject: [PATCH 061/139] Corrected dcoumentation which did not reflect the latest changes done within review process --- documentation/master-solicitor.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index c254f1de..221cb6ed 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1616,7 +1616,7 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.13.0:: * https://github.com/devonfw/solicitor/issues/191: Refactoring of the (experimental) Scancode integration (formerly class ScancodeFileAdapter). * https://github.com/devonfw/solicitor/issues/192: References to files within the sources of a component (e.g. license or notice files) -are now returned from the Scancode adapter by a local path prefixed by `$PKG_ROOT$/`. This replaces the usage of `file:` urls in this case. The same syntax needs to be used within curation data if the url pointing to license texts within the component needs to be curated. +are now returned from the Scancode adapter by a uri with schme/prefixed `pkgcontent:`. This replaces the usage of `file:` urls in this case. The same syntax needs to be used within curation data if the url pointing to license texts within the component needs to be curated. Changes in 1.12.0:: * https://github.com/devonfw/solicitor/issues/182: Add new data quality and traceability attributes to the ComponentInfo data structure. From 4a63078d7467ed3fe8bf5caf4ab14eb04463e561 Mon Sep 17 00:00:00 2001 From: duph97 Date: Sun, 20 Aug 2023 18:25:02 +0200 Subject: [PATCH 062/139] PackageUrlHandler for Nuget Packages (#197) Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../impl/NugetPackageURLHandlerImpl.java | 57 +++++++++++++++++++ .../reader/cyclonedx/CyclonedxReader.java | 2 +- .../src/main/resources/application.properties | 2 + .../impl/NugetPackageURLHandlerTests.java | 38 +++++++++++++ .../cyclonedx/CyclonedxReaderTests.java | 8 +-- documentation/master-solicitor.asciidoc | 3 +- 6 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/NugetPackageURLHandlerImpl.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/common/packageurl/impl/NugetPackageURLHandlerTests.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/NugetPackageURLHandlerImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/NugetPackageURLHandlerImpl.java new file mode 100644 index 00000000..6f8fb6db --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/packageurl/impl/NugetPackageURLHandlerImpl.java @@ -0,0 +1,57 @@ +package com.devonfw.tools.solicitor.common.packageurl.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.github.packageurl.PackageURL; + +@Component +public class NugetPackageURLHandlerImpl extends AbstractSingleKindPackageURLHandler { + private String repoBaseUrl; + + /** + * The constructor. + * + * @param repoBaseUrl the repository base url + */ + @Autowired + public NugetPackageURLHandlerImpl(@Value("${packageurls.nuget.repobaseurl}") String repoBaseUrl) { + + super(); + this.repoBaseUrl = repoBaseUrl; + } + + @Override + public boolean canHandle(PackageURL packageURL) { + + return (PackageURL.StandardTypes.NUGET.equals(packageURL.getType())); + } + + // Nuget does not have a standardized API to fetch the source code + // This needs to be handled by another component (LicenseUrlGuesser?) + @Override + public String doSourceDownloadUrlFor(PackageURL purl) { + + return null; + } + + // https://www.nuget.org/api/v2/package/{packageID}/{packageVersion} + // Example: https://www.nuget.org/api/v2/package/Castle.Core/4.4.1 + @Override + public String doPackageDownloadUrlFor(PackageURL purl) { + + StringBuffer sb = new StringBuffer(this.repoBaseUrl); // https://www.nuget.org/api/v2/package/ + sb.append(purl.getName()).append("/"); // {packageID}/ + sb.append(purl.getVersion()).append(".nupkg"); // {packageVersion} + + return sb.toString(); + } + + @Override + public String doSourceArchiveSuffixFor(PackageURL packageURL) { + + return "nupkg"; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java index bf6a8e1c..a95953a0 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java @@ -94,7 +94,7 @@ public void readInventory(String type, String sourceUrl, Application application // Fill purl try { // check if handler exists for the package type defined in purl - if (!this.delegatingPackageURLHandler.sourceDownloadUrlFor(purl).isEmpty()) { + if (!this.delegatingPackageURLHandler.pathFor(purl).isEmpty()) { appComponent.setPackageUrl(purl); } } catch (SolicitorPackageURLException ex) { diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index f146c658..746a22cf 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -28,6 +28,8 @@ packageurls.maven.repobaseurl=https://repo1.maven.org/maven2/ packageurls.npm.repobaseurl=https://registry.npmjs.org/ # Base URL for accessing pypi packages packageurls.pypi.repobaseurl=https://pypi.io/packages/ +# Base URL for accessing nuget packages +packageurls.nuget.repobaseurl=https://www.nuget.org/api/v2/package/ # the URL of the base config file solicitor.base-config-url=classpath:com/devonfw/tools/solicitor/config/solicitor_base.cfg diff --git a/core/src/test/java/com/devonfw/tools/solicitor/common/packageurl/impl/NugetPackageURLHandlerTests.java b/core/src/test/java/com/devonfw/tools/solicitor/common/packageurl/impl/NugetPackageURLHandlerTests.java new file mode 100644 index 00000000..587d57b7 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/common/packageurl/impl/NugetPackageURLHandlerTests.java @@ -0,0 +1,38 @@ +package com.devonfw.tools.solicitor.common.packageurl.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class NugetPackageURLHandlerTests { + @Test + void testSourceDownloadUrlFor() { + + NugetPackageURLHandlerImpl handler = new NugetPackageURLHandlerImpl("http://test/"); + assertEquals(null, handler.sourceDownloadUrlFor("pkg:nuget/com.someorg@4.5.35")); + } + + @Test + void testCanHandle() { + + NugetPackageURLHandlerImpl handler = new NugetPackageURLHandlerImpl("http://test/"); + assertTrue(handler.canHandle(handler.parse("pkg:nuget/a@1"))); + assertFalse(handler.canHandle(handler.parse("pkg:nuget1/a@1"))); + } + + @Test + void testSourceArchiveSuffixFor() { + + NugetPackageURLHandlerImpl handler = new NugetPackageURLHandlerImpl("http://test/"); + assertEquals("nupkg", handler.sourceArchiveSuffixFor("pkg:nuget/com.someorg@4.5.35")); + } + + @Test + void testPathFor() { + + NugetPackageURLHandlerImpl handler = new NugetPackageURLHandlerImpl("http://test/"); + assertEquals("pkg/nuget/com.someorg/4.5.35", handler.pathFor("pkg:nuget/com.someorg@4.5.35")); + } +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java index 560f436e..32667739 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java @@ -41,7 +41,7 @@ public class CyclonedxReaderTests { public void readMavenFileAndCheckSize() { // Always return a non-empty String for maven purls - Mockito.when(this.delegatingPurlHandler.sourceDownloadUrlFor(Mockito.startsWith("pkg:maven/"))).thenReturn("foo"); + 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", "Java8"); @@ -76,7 +76,7 @@ public void readMavenFileAndCheckSize() { public void readMavenFileAndCheckSizeNegative() { // Always throw exception for maven purls - Mockito.when(this.delegatingPurlHandler.sourceDownloadUrlFor(Mockito.startsWith("pkg:maven/"))).thenThrow( + Mockito.when(this.delegatingPurlHandler.pathFor(Mockito.startsWith("pkg:maven/"))).thenThrow( new SolicitorPackageURLException("No applicable SingleKindPackageURLHandler found for type 'maven'")); Application application = this.modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com", @@ -100,7 +100,7 @@ public void readMavenFileAndCheckSizeNegative() { public void readMavenFileAndCheckSingleContentSize() { // Always return a non-empty String for maven purls - Mockito.when(this.delegatingPurlHandler.sourceDownloadUrlFor(Mockito.startsWith("pkg:maven/"))).thenReturn("foo"); + 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", "Java8"); @@ -162,7 +162,7 @@ public void readMavenFileAndCheckSingleContentSize() { public void readNpmFileAndCheckSize() { // Always return a non-empty String for npm purls - Mockito.when(this.delegatingPurlHandler.sourceDownloadUrlFor(Mockito.startsWith("pkg:npm/"))).thenReturn("foo"); + Mockito.when(this.delegatingPurlHandler.pathFor(Mockito.startsWith("pkg:npm/"))).thenReturn("foo"); Application application = this.modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com", "Angular"); diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 221cb6ed..59227a20 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1614,9 +1614,10 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.13.0:: +* https://github.com/devonfw/solicitor/issues/198: Add PackageUrlHandler for nuget packages. * https://github.com/devonfw/solicitor/issues/191: Refactoring of the (experimental) Scancode integration (formerly class ScancodeFileAdapter). * https://github.com/devonfw/solicitor/issues/192: References to files within the sources of a component (e.g. license or notice files) -are now returned from the Scancode adapter by a uri with schme/prefixed `pkgcontent:`. This replaces the usage of `file:` urls in this case. The same syntax needs to be used within curation data if the url pointing to license texts within the component needs to be curated. +are now returned from the Scancode adapter by a local path prefixed by `$PKG_ROOT$/`. This replaces the usage of `file:` urls in this case. The same syntax needs to be used within curation data if the url pointing to license texts within the component needs to be curated. Changes in 1.12.0:: * https://github.com/devonfw/solicitor/issues/182: Add new data quality and traceability attributes to the ComponentInfo data structure. From 911a4c77470219133ca6a310986deb767676fd2f Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 21 Aug 2023 09:50:59 +0200 Subject: [PATCH 063/139] added word to spellcheck directory --- solicitor.dict | 1 + 1 file changed, 1 insertion(+) diff --git a/solicitor.dict b/solicitor.dict index f7fd7832..a4b1fc7f 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -30,6 +30,7 @@ scancode SOLI sql Unlicense +uri url urls velo From 050cc7286a17c579f6206cfe18ca5df58b8b5d5b Mon Sep 17 00:00:00 2001 From: duph97 Date: Wed, 30 Aug 2023 17:08:31 +0200 Subject: [PATCH 064/139] Source download script refactoring (#200) Co-authored-by: Oliver Hecker <8004361+ohecker@users.noreply.github.com> --- .../tools/solicitor/config/solicitor_base.cfg | 2 +- .../templates/Source_Download_Script.vm | 105 ++++++------------ documentation/master-solicitor.asciidoc | 4 + 3 files changed, 39 insertions(+), 72 deletions(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg index 549ec9a2..aaf0ad4e 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg +++ b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg @@ -184,7 +184,7 @@ "type" : "velo", "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Source_Download_Script.vm", "target" : "${cfgdir}/output/download_sources_${project}.sh", - "description" : "Script for downloading sources which need to be included in the distribution from Maven-Central and NpmRepository", + "description" : "Script for downloading sources which need to be included in the distribution", "dataTables" : { "MODELROOT" : "classpath:com/devonfw/tools/solicitor/sql/modelroot.sql", "ARTIFACTS" : "classpath:com/devonfw/tools/solicitor/sql/sources_tobeincluded.sql" diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/Source_Download_Script.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Source_Download_Script.vm index 68f58e52..d42c95a8 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/Source_Download_Script.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Source_Download_Script.vm @@ -1,77 +1,40 @@ ## SPDX-License-Identifier: Apache-2.0 # # This script was generated by 'Solicitor', v$MODELROOT.getDataRow(0).solicitorVersion ($MODELROOT.getDataRow(0).solicitorGitHash) on $MODELROOT.getDataRow(0).executionTime -# see https://github.com/devonfw-forge/solicitor -# +# see https://github.com/devonfw/solicitor +# This script downloads the sources for components, which have the "includeSource" flag set to "yes". + #foreach ($artifact in $ARTIFACTS) # Application: $artifact.applicationName -- Group: $artifact.groupId -- Artifact: $artifact.artifactId -- Version: $artifact.version -- License(s): $artifact.effectiveNormalizedLicense -- includeSource: $artifact.includeSource -mkdir $artifact.applicationName &> /dev/null -cd $artifact.applicationName -#[[SD=$(pwd)]]# -#if ( $artifact.artifactId.toString().contains("/") ) -cutArtifact='${artifact.artifactId}' -#if ( $artifact.repoType == "npm" ) -curl -# https://registry.npmjs.org/$artifact.artifactId/-/#[[${cutArtifact#*/}]]#-${artifact.version}.tgz -o #[[${cutArtifact#*/}]]#-${artifact.version}.tgz -tar -xvzf #[[${cutArtifact#*/}]]#-${artifact.version}.tgz -if [ $? -ne 0 ] -then - echo "Need to retrieve-manually" > #[[${cutArtifact%/*}+${cutArtifact#*/}]]#-${artifact.version}.tgz--need-to-retrieve-manually-npm - rm #[[${cutArtifact#*/}]]#-${artifact.version}.tgz -fi -rm -rf package/ -#elseif ( $artifact.repoType == "yarn" ) -curl -# https://registry.yarnpkg.com/$artifact.artifactId/-/#[[${cutArtifact#*/}]]#-${artifact.version}.tgz -o #[[${cutArtifact#*/}]]#-${artifact.version}.tgz -tar -xvzf #[[${cutArtifact#*/}]]#-${artifact.version}.tgz -if [ $? -ne 0 ] -then - echo "Need to retrieve-manually" > #[[${cutArtifact%/*}+${cutArtifact#*/}]]#-${artifact.version}.tgz--need-to-retrieve-manually-yarn - rm #[[${cutArtifact#*/}]]#-${artifact.version}.tgz -fi -rm -rf package/ -#else -echo "Need to retrieve-manually (unknown repoType" > $artifact.groupId--#[[${cutArtifact%/*}+${cutArtifact#*/}]]#-${artifact.version}.tgz--need-to-retrieve-manually -#end -#else -#if ( $artifact.repoType == "maven" ) -curl -# https://repo1.maven.org/maven2/$artifact.groupIdT/$artifact.artifactId/$artifact.version/$artifact.artifactId-$artifact.version-sources.jar -o $artifact.groupId--$artifact.artifactId-$artifact.version-sources.jar -unzip -q -t $artifact.groupId--$artifact.artifactId-$artifact.version-sources.jar -if [ $? -ne 0 ] -then - echo "Need to retrieve-manually" > $artifact.groupId--$artifact.artifactId-$artifact.version-sources.jar--need-to-retrieve-manually-maven - rm $artifact.groupId--$artifact.artifactId-$artifact.version-sources.jar -fi -#elseif ( $artifact.repoType == "npm" ) -curl -# https://registry.npmjs.org/$artifact.artifactId/-/$artifact.artifactId-${artifact.version}.tgz -o $artifact.artifactId-${artifact.version}.tgz -tar -xvzf $artifact.artifactId-${artifact.version}.tgz -if [ $? -ne 0 ] -then - echo "Need to retrieve-manually" > $artifact.artifactId-${artifact.version}.tgz--need-to-retrieve-manually-npm - rm $artifact.artifactId-${artifact.version}.tgz -fi -rm -rf package/ -#elseif ( $artifact.repoType == "pip" ) -art='${artifact.artifactId}' -#[[firstChar=${art:0:1}]]# -curl -# -L https://pypi.io/packages/source/#[[$firstChar]]#/$artifact.artifactId/$artifact.artifactId-${artifact.version}.tar.gz -o $artifact.artifactId-${artifact.version}.tar.gz -tar -xvzf $artifact.artifactId-${artifact.version}.tar.gz -if [ $? -ne 0 ] -then - echo "Need to retrieve-manually" > $artifact.artifactId-${artifact.version}.tar.gz--need-to-retrieve-manually-pip - rm $artifact.artifactId-${artifact.version}.tar.gz -fi -rm -rf $artifact.artifactId-${artifact.version}/ -#elseif ( $artifact.repoType == "yarn" ) -curl -# https://registry.yarnpkg.com/$artifact.artifactId/-/$artifact.artifactId-${artifact.version}.tgz -o $artifact.artifactId-${artifact.version}.tgz -tar -xvzf $artifact.artifactId-${artifact.version}.tgz -if [ $? -ne 0 ] -then - echo "Need to retrieve-manually" > $artifact.artifactId-${artifact.version}.tgz--need-to-retrieve-manually-npm - rm $artifact.artifactId-${artifact.version}.tgz -fi -rm -rf package/ -#else -echo "Need to retrieve-manually (unknown repoType)" > $artifact.groupId--$artifact.artifactId-${artifact.version}--need-to-retrieve-manually -#end + mkdir $artifact.applicationName &> /dev/null + cd $artifact.applicationName + #if ( $artifact.packageUrl == "NA" ) + echo "No PackageUrl available: Artifact Group: $artifact.groupId -- Artifact: $artifact.artifactId -- Version: $artifact.version" >> need-to-retrieve-manually.txt + #else + #set($sourceArchiveSuffix= ${purlhandler.sourceArchiveSuffixFor($artifact.packageUrl)}) + #set($prefixToRemove = "pkg-") + + #set($fileName= ${purlhandler.pathFor($artifact.packageUrl).replaceAll("/","-")}) + #set($fileName= $fileName+("-sources.")) + #set($fileName= $fileName + $sourceArchiveSuffix) + #set($fileName = $fileName.substring($prefixToRemove.length())) + # File name: $fileName + + curl -# $purlhandler.sourceDownloadUrlFor($artifact.packageUrl) -o $fileName + #if ( $sourceArchiveSuffix == "jar" ) + unzip -q -t $fileName + #elseif($sourceArchiveSuffix == "tgz") + tar -xvzf $fileName -O > /dev/null + #elseif($sourceArchiveSuffix == "tar.gz") + tar -xvzf $fileName -O > /dev/null + #else + unzip -q nonexisting + #end + if [ $? -ne 0 ] + then + echo "Could not be downloaded: $fileName " >> need-to-retrieve-manually.txt + rm $fileName + fi + #end + cd .. #end -cd .. -#end \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 59227a20..b1c68170 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1534,6 +1534,9 @@ Similar to the above but uses guessed license URLs and content, see < Date: Wed, 30 Aug 2023 17:19:13 +0200 Subject: [PATCH 065/139] Version 1.13.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 55b93658..00993609 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.13.0-SNAPSHOT + 1.13.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 4965ec33..931ede80 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.13.0-SNAPSHOT + 1.13.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 13b9d1b5..a06de1b4 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.13.0-SNAPSHOT + 1.13.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 8348ccc5..3137303b 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.13.0-SNAPSHOT + 1.13.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index f98c1098..66eb7650 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.13.0-SNAPSHOT + 1.13.0 pom Solicitor Aggregator From 4dd0a532ee1a4caafaf18f02c5b7c81dead1ca50 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 30 Aug 2023 17:38:40 +0200 Subject: [PATCH 066/139] Set version to SNAPSHOT of next minor release --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 4 +++- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 00993609..b9c6c607 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.13.0 + 1.14.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 931ede80..676d595a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.13.0 + 1.14.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index b1c68170..7b76d4a9 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1616,11 +1616,13 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.14.0:: + Changes in 1.13.0:: * https://github.com/devonfw/solicitor/issues/198: Add PackageUrlHandler for nuget packages. * https://github.com/devonfw/solicitor/issues/191: Refactoring of the (experimental) Scancode integration (formerly class ScancodeFileAdapter). * https://github.com/devonfw/solicitor/issues/192: References to files within the sources of a component (e.g. license or notice files) -are now returned from the Scancode adapter by a local path prefixed by `$PKG_ROOT$/`. This replaces the usage of `file:` urls in this case. The same syntax needs to be used within curation data if the url pointing to license texts within the component needs to be curated. +are now returned from the Scancode adapter by a uri with schme/prefixed `pkgcontent:`. This replaces the usage of `file:` urls in this case. The same syntax needs to be used within curation data if the url pointing to license texts within the component needs to be curated. * https://github.com/devonfw/solicitor/issues/199: Refactoring of Source_Download_Script.vm to use packageUrl instead of repoType. Changes in 1.12.0:: diff --git a/documentation/pom.xml b/documentation/pom.xml index a06de1b4..cf44e24d 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.13.0 + 1.14.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 3137303b..43e6ed7a 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.13.0 + 1.14.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 66eb7650..4b97da58 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.13.0 + 1.14.0-SNAPSHOT pom Solicitor Aggregator From 74531c33c45b3d23114210fb08d9d4f3134d3cc0 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 1 Sep 2023 09:33:59 +0200 Subject: [PATCH 067/139] Fix of documentation: Update config files listed in the user guide to the latest version --- documentation/files/application.properties | 2 ++ documentation/files/solicitor_base.cfg | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/documentation/files/application.properties b/documentation/files/application.properties index f146c658..746a22cf 100644 --- a/documentation/files/application.properties +++ b/documentation/files/application.properties @@ -28,6 +28,8 @@ packageurls.maven.repobaseurl=https://repo1.maven.org/maven2/ packageurls.npm.repobaseurl=https://registry.npmjs.org/ # Base URL for accessing pypi packages packageurls.pypi.repobaseurl=https://pypi.io/packages/ +# Base URL for accessing nuget packages +packageurls.nuget.repobaseurl=https://www.nuget.org/api/v2/package/ # the URL of the base config file solicitor.base-config-url=classpath:com/devonfw/tools/solicitor/config/solicitor_base.cfg diff --git a/documentation/files/solicitor_base.cfg b/documentation/files/solicitor_base.cfg index 7e4c9a8c..aaf0ad4e 100644 --- a/documentation/files/solicitor_base.cfg +++ b/documentation/files/solicitor_base.cfg @@ -161,9 +161,9 @@ } },{ "type" : "velo", - "templateSource" : "file:${cfgdir}/templates/Pflichtangaben.vm", - "target" : "${cfgdir}/output/Pflichtangaben_${project}.html", - "description" : "Das Dokument mit Pflichtangaben", + "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Attributions.vm", + "target" : "${cfgdir}/output/Attributions_${project}.html", + "description" : "A document containing the license information and attributions required by the OSS licenses", "dataTables" : { "ENGAGEMENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_engagements.sql", "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql", @@ -184,7 +184,7 @@ "type" : "velo", "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Source_Download_Script.vm", "target" : "${cfgdir}/output/download_sources_${project}.sh", - "description" : "Script for downloading sources which need to be included in the distribution from Maven-Central and NpmRepository", + "description" : "Script for downloading sources which need to be included in the distribution", "dataTables" : { "MODELROOT" : "classpath:com/devonfw/tools/solicitor/sql/modelroot.sql", "ARTIFACTS" : "classpath:com/devonfw/tools/solicitor/sql/sources_tobeincluded.sql" From 02a12b7aa47147a14b02d64a719178c896c9de08 Mon Sep 17 00:00:00 2001 From: malkam <108339027+mahmoudAlkam@users.noreply.github.com> Date: Sun, 17 Sep 2023 18:09:31 +0200 Subject: [PATCH 068/139] Get curation data from alternative location (#201) Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../componentinfo/ComponentInfo.java | 1 + .../ComponentInfoInventoryProcessor.java | 10 ++-- .../componentinfo/ComponentInfoProvider.java | 4 +- .../curation/ComponentInfoCurator.java | 8 ++- .../curation/ComponentInfoCuratorImpl.java | 19 +++--- .../CuratingComponentInfoAdapter.java | 14 +++-- .../curation/CurationProvider.java | 9 ++- .../curation/SingleFileCurationProvider.java | 9 +-- ...ncuratedScancodeComponentInfoProvider.java | 8 ++- .../ScancodeComponentInfoAdapterTest.java | 60 +++++++++++++++---- documentation/master-solicitor.asciidoc | 3 +- 11 files changed, 101 insertions(+), 44 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java index 8409a6c6..6104fbc4 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java @@ -86,4 +86,5 @@ public interface ComponentInfo { * @return the traceability notes */ List getTraceabilityNotes(); + } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index b8bbf6c9..2f40a84f 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -91,7 +91,7 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { ComponentInfo componentInfo = null; try { for (ComponentInfoProvider cia : this.componentInfoAdapters) { - componentInfo = cia.getComponentInfo(ac.getPackageUrl()); + componentInfo = cia.getComponentInfo(ac.getPackageUrl(), null); // stop querying further adapters if some info was returned if (componentInfo != null) { break; @@ -140,7 +140,7 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { if (componentInfo.getHomepageUrl() != null) { ac.setOssHomepage(componentInfo.getHomepageUrl()); } - // check whether Source Reop Url is included in input file or not + // check whether Source ReopUrl is included in input file or not if (componentInfo.getSourceRepoUrl() != null) { ac.setSourceRepoUrl(componentInfo.getSourceRepoUrl()); } @@ -159,11 +159,11 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { } /** - * Formats the traceability notes from the given {@link ComponentInfo} by concatenating to a single string using line + * Formats the traceabilityNotes from the given {@link ComponentInfo} by concatenating to a single string using line * separators. * - * @param componentInfo The {@link ComponentInfo} containing the traceability notes. - * @return A formatted {@link String} representing the traceability notes, separated by the long separator. + * @param componentInfo The {@link ComponentInfo} containing the traceabilityNotes. + * @return A formatted {@link String} representing the traceabilityNotes, separated by the long separator. */ public String formatTraceabilityNotes(ComponentInfo componentInfo) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java index f904a849..22df6792 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java @@ -12,10 +12,12 @@ public interface ComponentInfoProvider { * {@link ComponentInfo} object. * * @param packageUrl The identifier of the package for which information is requested + * @param curationDataSelector identifies which source should be used for the curation data. null + * indicates that the default should be used. * @return the data for the component. null is returned if no data is available, * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be returned in such a case. */ - ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException; + ComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) throws ComponentInfoAdapterException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java index 95148cb6..a0b60598 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java @@ -13,9 +13,11 @@ public interface ComponentInfoCurator { * Curates the given {@link ComponentInfo}. * * @param componentInfo the componentInfo to curate - * @return the curated component info or - if no curations are done - the incoming object - * @throws ComponentInfoAdapterException if the curations could not be read + * @param curationDataSelector identifies which source should be used for the curation data. null + * indicates that the default should be used. + * @return the curated component info or - if no curation are done - the incoming object + * @throws ComponentInfoAdapterException if the curation could not be read */ - ComponentInfo curate(ComponentInfo componentInfo) throws ComponentInfoAdapterException; + ComponentInfo curate(ComponentInfo componentInfo, String curationDataSelector) throws ComponentInfoAdapterException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java index ca7d7e51..b89ce6c5 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java @@ -30,7 +30,7 @@ public class ComponentInfoCuratorImpl implements ComponentInfoCurator { /** * The constructor. * - * @param curationProvider the curation provider used to get the curations + * @param curationProvider the curation provider used to get the curation * @param componentContentProvider the provider used for loading referenced subpath data from the component */ @Autowired @@ -42,18 +42,22 @@ public ComponentInfoCuratorImpl(CurationProvider curationProvider, } /** - * Checks for the existence of curations for the given package via the {@link CurationProvider}. If curations exist - * then the a new curated {@link ComponentInfo} instance will be created from the incoming uncurated - * {@link ComponentInfo} and the curations. + * Checks for the existence of curation for the given package via the {@link CurationProvider}. If curations exist + * then a new curated {@link ComponentInfo} instance will be created from the incoming uncurated {@link ComponentInfo} + * and the curation. * * @param componentInfo the componentInfo to curate + * @param curationDataSelector identifies which source should be used for the curation data. null + * indicates that the default should be used. * @return the curated component info - * @throws ComponentInfoAdapterException if the curations could not be read + * @throws ComponentInfoAdapterException if the curation could not be read */ @Override - public ComponentInfo curate(ComponentInfo componentInfo) throws ComponentInfoAdapterException { + public ComponentInfo curate(ComponentInfo componentInfo, String curationDataSelector) + throws ComponentInfoAdapterException { - ComponentInfoCuration foundCuration = this.curationProvider.findCurations(componentInfo.getPackageUrl()); + ComponentInfoCuration foundCuration = this.curationProvider.findCurations(componentInfo.getPackageUrl(), + curationDataSelector); if (foundCuration != null) { DefaultComponentInfoImpl componentInfoImpl = new DefaultComponentInfoImpl(componentInfo); applyFoundCurations(componentInfoImpl, foundCuration); @@ -61,7 +65,6 @@ public ComponentInfo curate(ComponentInfo componentInfo) throws ComponentInfoAda } else { return componentInfo; } - } private void applyFoundCurations(DefaultComponentInfoImpl componentInfo, ComponentInfoCuration curation) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java index 147506e5..4061aa54 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java @@ -29,25 +29,29 @@ public CuratingComponentInfoAdapter(UncuratedComponentInfoProvider uncuratedComp } /** - * Retrieves the component information and curations for a package identified by the given package URL. Returns the + * Retrieves the component information and curation for a package identified by the given package URL. Returns the * data as a {@link ComponentInfo} object. * * @param packageUrl The identifier of the package for which information is requested - * @return the data derived from the scancode results after applying any defined curations. null is + * @param curationDataSelector identifies which source should be used for the curation data. null + * indicates that the default should be used. + * @return the data derived from the scancode results after applying any defined curation. null is * returned if no data is available, * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be return in such a case. */ @Override - public ComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException { + public ComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) + throws ComponentInfoAdapterException { if (isFeatureActive()) { - ComponentInfo componentInfo = this.uncuratedComponentInfoProvider.getComponentInfo(packageUrl); + ComponentInfo componentInfo = this.uncuratedComponentInfoProvider.getComponentInfo(packageUrl, + curationDataSelector); if (componentInfo == null) { return null; } - componentInfo = this.componentInfoCurator.curate(componentInfo); + componentInfo = this.componentInfoCurator.curate(componentInfo, curationDataSelector); return componentInfo; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java index a916d2c7..0ef12c63 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java @@ -11,12 +11,15 @@ public interface CurationProvider { /** - * Return the curation data for a given package + * Return the curation data for a given package. * * @param packageUrl identifies the package - * @return the curation data if it exists or null if no curations exist for the package. + * @param curationDataSelector identifies which source should be used for the curation data. null + * indicates that the default should be used. + * @return the curation data if it exists or null if no curation exist for the package. * @throws ComponentInfoAdapterException if something unexpected happens */ - ComponentInfoCuration findCurations(String packageUrl) throws ComponentInfoAdapterException; + ComponentInfoCuration findCurations(String packageUrl, String curationDataSelector) + throws ComponentInfoAdapterException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java index 10c5845c..169ec7c2 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java @@ -53,14 +53,16 @@ public SingleFileCurationProvider(AllKindsPackageURLHandler packageURLHandler) { } /** - * Return the curation data for a given package + * Return the curation data for a given package. * * @param packageUrl identifies the package - * @return the curation data if it existes or null if no curations exist for the package. + * @param curationDataSelector identifies which source should be used for the curation data. This parameter is ignored + * by this implementation. * @throws ComponentInfoAdapterException if something unexpected happens */ @Override - public ComponentInfoCuration findCurations(String packageUrl) throws ComponentInfoAdapterException { + public ComponentInfoCuration findCurations(String packageUrl, String curationDataSelector) + throws ComponentInfoAdapterException { ComponentInfoCuration foundCuration = null; @@ -94,7 +96,6 @@ public ComponentInfoCuration findCurations(String packageUrl) throws ComponentIn } catch (IOException e) { throw new ComponentInfoAdapterException("Could not read Curations JSON", e); } - } return foundCuration; } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java index 0c3a9984..c4a95aba 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java @@ -94,12 +94,15 @@ public void setMinLicensefileNumberOfLines(int minLicensefileNumberOfLines) { * Read scancode information for the given package from local file storage. * * @param packageUrl The package url of the package + * @param curationDataSelector identifies which source should be used for the curation data. null + * indicates that the default should be used. * @return the read scancode information, null if no information was found * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be return in such a case. */ @Override - public ScancodeComponentInfo getComponentInfo(String packageUrl) throws ComponentInfoAdapterException { + public ScancodeComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) + throws ComponentInfoAdapterException { ScancodeRawComponentInfo rawScancodeData = this.fileScancodeRawComponentInfoProvider.readScancodeData(packageUrl); if (rawScancodeData == null) { @@ -257,8 +260,7 @@ private String normalizeLicenseUrl(String packageUrl, String licenseUrl) { "https://scancode-licensedb.aboutcode.org"); adjustedLicenseUrl = adjustedLicenseUrl.replace("github.com", "raw.github.com").replace("/tree", ""); } else if (this.fileScancodeRawComponentInfoProvider.isLocalContentPath(packageUrl, licenseUrl)) { - adjustedLicenseUrl = this.fileScancodeRawComponentInfoProvider.pkgContentUriFromPath(packageUrl, - licenseUrl); + adjustedLicenseUrl = this.fileScancodeRawComponentInfoProvider.pkgContentUriFromPath(packageUrl, licenseUrl); LOG.debug("LOCAL LICENSE: " + licenseUrl); } } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java index b08f0c85..4d30fdcf 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java @@ -4,10 +4,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; @@ -17,6 +20,7 @@ import com.devonfw.tools.solicitor.componentinfo.LicenseInfo; import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCurator; import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCuratorImpl; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationProvider; import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; /** @@ -67,9 +71,9 @@ public void setup() { } /** - * Test the {@link ScancodeFileAdapter#getComponentInfo(String)} method when such package is known. + * Test the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} method when such package is known. * - * @throws ComponentInfoAdapterException + * @throws ComponentInfoAdapterException if something goes wrong */ @Test public void testGetComponentInfoNaPackage() throws ComponentInfoAdapterException { @@ -78,16 +82,17 @@ public void testGetComponentInfoNaPackage() throws ComponentInfoAdapterException // when ComponentInfo componentInfo = this.scancodeComponentInfoAdapter - .getComponentInfo("pkg:maven/com.devonfw.tools/unknown@0.1.0"); + .getComponentInfo("pkg:maven/com.devonfw.tools/unknown@0.1.0", "someCurationSelector"); // then assertNull(componentInfo); } /** - * Test the {@link ScancodeFileAdapter#getComponentInfo(String)} method when no curations are available. + * Test the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} method when no curations are + * available. * - * @throws ComponentInfoAdapterException + * @throws ComponentInfoAdapterException if something goes wrong */ @Test public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterException { @@ -96,8 +101,8 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/nonexisting.yaml"); // when - ComponentInfo componentInfo = this.scancodeComponentInfoAdapter - .getComponentInfo("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0"); + ComponentInfo componentInfo = this.scancodeComponentInfoAdapter.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); // then assertNotNull(componentInfo); @@ -133,16 +138,49 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx } /** - * Test the {@link ScancodeFileAdapter#getComponentInfo(String)} method when curations are existing. + * Test if the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} propagates the parameter + * curationDataSelector to downstream beans. + * + * @throws ComponentInfoAdapterException if something goes wrong + */ + @Test + public void testGetComponentCheckCurationDataSelector() throws ComponentInfoAdapterException { + + // given + CurationProvider curationProvider = Mockito.mock(CurationProvider.class); + when(curationProvider.findCurations(Mockito.anyString(), Mockito.anyString())).thenReturn(null); + this.componentInfoCuratorImpl = new ComponentInfoCuratorImpl(curationProvider, + this.fileScancodeRawComponentInfoProvider); + + this.scancodeComponentInfoAdapter = new ScancodeComponentInfoAdapter(this.uncuratedScancodeComponentInfoProvider, + this.componentInfoCuratorImpl); + this.scancodeComponentInfoAdapter.setFeatureFlag(true); + + // when + ComponentInfo componentInfo = this.scancodeComponentInfoAdapter.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + + // then + assertNotNull(componentInfo); + + ArgumentCaptor captor1 = ArgumentCaptor.forClass(String.class); + ArgumentCaptor captor2 = ArgumentCaptor.forClass(String.class); + Mockito.verify(curationProvider, times(1)).findCurations(captor1.capture(), captor2.capture()); + assertEquals("someCurationSelector", captor2.getValue()); + + } + + /** + * Test the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} method when curations are existing. * - * @throws ComponentInfoAdapterException + * @throws ComponentInfoAdapterException if something goes wrong */ @Test public void testGetComponentInfoWithCurations() throws ComponentInfoAdapterException { // when - ComponentInfo componentInfo = this.scancodeComponentInfoAdapter - .getComponentInfo("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0"); + ComponentInfo componentInfo = this.scancodeComponentInfoAdapter.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); // then assertNotNull(componentInfo); diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 7b76d4a9..04452309 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1535,7 +1535,7 @@ This template creates an HTML document which contains OSS components that have b Application, OSS Name/Product, OSS ArtifactId, OSS Version, Effective Normalized Licenses, License Count. === Source_Download_Script.vm -This template creates a bash script for downloading package sources for all packages where the license requires the source code to be included in the distribution. +This template creates a bash script for downloading package sources for all packages where the license requires the source code to be included in the distribution. === ScancodeScript.vm, ScancodeScanScript.vm These templates create script files for downloading package sources and using https://github.com/nexB/scancode-toolkit[ScanCode] to do a "deep license scan" @@ -1617,6 +1617,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.14.0:: +* https://github.com/devonfw/solicitor/issues/202: Include parameter in CurationProvider.findCurations() to allow getting curation from alternative locations. Changes in 1.13.0:: * https://github.com/devonfw/solicitor/issues/198: Add PackageUrlHandler for nuget packages. From 8ccfe3e405d60c86e01416c6bc098d47ac57edd0 Mon Sep 17 00:00:00 2001 From: duph97 Date: Fri, 6 Oct 2023 18:06:15 +0200 Subject: [PATCH 069/139] Feature/deprecate repo type (#204) --- .../tools/solicitor/config/ConfigFactory.java | 8 +++++++ .../tools/solicitor/config/ReaderConfig.java | 2 +- .../model/ModelImporterExporter.java | 5 ++++- .../inventory/ApplicationComponentImpl.java | 10 +++++++-- .../model/inventory/ApplicationComponent.java | 7 ++++-- .../tools/solicitor/config/solicitor_base.cfg | 3 +-- .../samples/generic_maven_sample.cfg | 3 +-- .../resources/samples/solicitor_sample.cfg | 3 +-- .../src/main/resources/starters/solicitor.cfg | 3 +-- documentation/master-solicitor.asciidoc | 22 +++++++------------ 10 files changed, 38 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java b/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java index 5c441378..ce58ec3c 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java @@ -23,6 +23,7 @@ import com.devonfw.tools.solicitor.SolicitorSetup; import com.devonfw.tools.solicitor.SolicitorSetup.ReaderSetup; +import com.devonfw.tools.solicitor.common.DeprecationChecker; import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.model.ModelFactory; import com.devonfw.tools.solicitor.model.ModelRoot; @@ -53,6 +54,9 @@ public class ConfigFactory { @Autowired private ModelFactory modelFactory; + @Autowired + private DeprecationChecker deprecationChecker; + @Value("${solicitor.base-config-url}") private String baseConfigUrl; @@ -121,6 +125,10 @@ public ModelRoot createConfig(String url) { rs.setType(rc.getType()); rs.setSource(rc.getSource()); rs.setUsagePattern(rc.getUsagePattern()); + if (rc.getRepoType() != null) { + this.deprecationChecker.check(true, + "The parameter 'repoType' in the reader configuration is deprecated, see https://github.com/devonfw/solicitor/issues/190"); + } rs.setRepoType(rc.getRepoType()); rs.setConfiguration(rc.getConfiguration()); rs = resolvePlaceholdersInReader(rs, placeHolderMap); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/config/ReaderConfig.java b/core/src/main/java/com/devonfw/tools/solicitor/config/ReaderConfig.java index 78dde661..3ce7d926 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/config/ReaderConfig.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/config/ReaderConfig.java @@ -14,7 +14,7 @@ * Represents the configuration of a {@link Reader} in JSON format. */ public class ReaderConfig { - private static final String DEFAULT_REPO_TYPE = "maven"; + private static final String DEFAULT_REPO_TYPE = null; @JsonProperty private String type; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java index 32d0c836..37152e77 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java @@ -158,8 +158,11 @@ private void readApplicationComponents(ApplicationImpl application, JsonNode app applicationComponent.setGroupId(groupId); applicationComponent.setArtifactId(artifactId); applicationComponent.setVersion(version); - applicationComponent.setRepoType(repoType); applicationComponent.setPackageUrl(packageUrl); + // when reading from file we set repoType after packageUrl to make sure the effective value is what is given by + // the file. (ApplicationComponent.setPackageUrl() will set repoType as well if it is not already set to something + // different from null) + applicationComponent.setRepoType(repoType); applicationComponent.setCopyrights(copyrights); applicationComponent.setNoticeFileUrl(noticeFileUrl); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java index f5016e1a..5f0ceb86 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/impl/inventory/ApplicationComponentImpl.java @@ -347,12 +347,18 @@ public void setPackageUrl(String packageUrl) { // Assures we have the canonical representation and the packageUrl is valid; if (packageUrl != null) { try { - this.packageUrl = new PackageURL(packageUrl).toString(); + PackageURL pUrl = new PackageURL(packageUrl); + this.packageUrl = pUrl.toString(); + if (this.repoType == null) { + // if repoType is not set then set it via the type of the PackageURL + this.repoType = pUrl.getType(); + } } catch (MalformedPackageURLException e) { throw new SolicitorRuntimeException("The given packageUrl '" + packageUrl + "' has an invalid format", e); } + } else { + this.packageUrl = null; } - this.packageUrl = packageUrl; } /** {@inheritDoc} */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java index 5280e349..8e9819ce 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/inventory/ApplicationComponent.java @@ -4,6 +4,7 @@ import com.devonfw.tools.solicitor.model.masterdata.Application; import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; +import com.github.packageurl.PackageURL; /** * Represents an ApplicationComponent in the Solicitor data model. @@ -231,7 +232,8 @@ public interface ApplicationComponent { void setVersion(String version); /** - * This method sets the field repoType. + * This method sets the field repoType. Note that this field might also be set under certain conditions + * via {@link #setPackageUrl(String)}. * * @param repoType the new value of the field repoType */ @@ -245,7 +247,8 @@ public interface ApplicationComponent { void setCopyrights(String copyrights); /** - * This method sets the field packageUrl. + * This method sets the field packageUrl. In case that the field repoType is not yet set + * (null) then it will also be set by the value obtained by {@link PackageURL#getType()}. * * @param packageUrl the new value of the field packageUrl. * diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg index aaf0ad4e..89942a24 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg +++ b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg @@ -16,8 +16,7 @@ "readers" : [ { "type" : "maven", "source" : "-", - "usagePattern" : "DYNAMIC_LINKING", - "repoType" : "maven" + "usagePattern" : "DYNAMIC_LINKING" } ] } ], "rules" : [ { diff --git a/core/src/main/resources/samples/generic_maven_sample.cfg b/core/src/main/resources/samples/generic_maven_sample.cfg index 150b26f0..aab88878 100644 --- a/core/src/main/resources/samples/generic_maven_sample.cfg +++ b/core/src/main/resources/samples/generic_maven_sample.cfg @@ -16,8 +16,7 @@ "readers" : [ { "type" : "maven", "source" : "file:licenses.xml", - "usagePattern" : "DYNAMIC_LINKING", - "repoType" : "maven" + "usagePattern" : "DYNAMIC_LINKING" } ] } ] } \ No newline at end of file diff --git a/core/src/main/resources/samples/solicitor_sample.cfg b/core/src/main/resources/samples/solicitor_sample.cfg index e7a291bb..9a1c2f77 100644 --- a/core/src/main/resources/samples/solicitor_sample.cfg +++ b/core/src/main/resources/samples/solicitor_sample.cfg @@ -16,8 +16,7 @@ "readers" : [ { "type" : "maven", "source" : "classpath:samples/licenses_solicitor.xml", - "usagePattern" : "DYNAMIC_LINKING", - "repoType" : "maven" + "usagePattern" : "DYNAMIC_LINKING" } ] } ] } \ No newline at end of file diff --git a/core/src/main/resources/starters/solicitor.cfg b/core/src/main/resources/starters/solicitor.cfg index 1fed04c5..c6a40da8 100644 --- a/core/src/main/resources/starters/solicitor.cfg +++ b/core/src/main/resources/starters/solicitor.cfg @@ -16,8 +16,7 @@ "readers" : [ { "type" : "maven", "source" : "file:${cfgdir}/input/licenses_starter.xml", - "usagePattern" : "DYNAMIC_LINKING", - "repoType" : "maven" + "usagePattern" : "DYNAMIC_LINKING" } ] } ] } \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 04452309..a0c17446 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -350,7 +350,7 @@ Within this section the different applications (=deliverables) of the engagement <6> the type of reader; for possible values see <> <7> location of the source file to read (ResourceLoader-URL) <8> usage pattern; possible values: DYNAMIC_LINKING, STATIC_LINKING, STANDALONE_PRODUCT -<9> repoType: Repository to download the sources from: currently possible values: maven, npm, pip, yarn, ort; if omitted then "maven" will be taken as default +<9> repoType: `repoType` to be set in the `ApplicationComponent` . *This parameter is deprecated and should no longer be used*, see <>. The value of `repoType` in `ApplicationComponent` will otherwise be determined from the type info in the PackageURL of the component. <10> _placeholder patterns might be used here_ @@ -644,8 +644,7 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "npm-license-checker", "source" : "file:path/to/licenses.json", - "usagePattern" : "STATIC_LINKING", - "repoType" : "npm" + "usagePattern" : "STATIC_LINKING" } ] ---- @@ -682,8 +681,7 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "npm-license-crawler-csv", "source" : "file:path/to/licenses.csv", - "usagePattern" : "STATIC_LINKING", - "repoType" : "npm" + "usagePattern" : "STATIC_LINKING" } ] ---- @@ -711,8 +709,7 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "yarn", "source" : "file:path/to/yarnlicenses.json", - "usagePattern" : "STATIC_LINKING", - "repoType" : "yarn" + "usagePattern" : "STATIC_LINKING" } ] ---- @@ -744,8 +741,7 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "pip", "source" : "file:path/to/piplicenses.json", - "usagePattern" : "DYNAMIC_LINKING", - "repoType" : "pip" + "usagePattern" : "DYNAMIC_LINKING" } ] ---- @@ -786,8 +782,7 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "ort", "source" : "file:path/to/analyzer-result.json", - "usagePattern" : "DYNAMIC_LINKING", - "repoType" : "ort" + "usagePattern" : "DYNAMIC_LINKING" } ] ---- @@ -919,14 +914,11 @@ In Solicitor, the data is read with the following part of the config "type" : "cyclonedx", "source" : "file:$/input/sbom.json", "usagePattern" : "DYNAMIC_LINKING" - "repoType" : "maven" //or any other valid repoType } ] ---- NOTE: Currently, Solicitor only has packageUrlHandlers for maven, npm and pip. For all other package types, Solicitor will ignore the packageUrl. -NOTE: In the "repoType" field, only ONE type is allowed to be set. In case the SBOM contains packages of multiple types, create a separate SBOM and reader configuration for each type. - == Working with Decision Tables Solicitor uses the Drools rule engine to execute business rules. Business rules are @@ -1339,6 +1331,7 @@ The following features are deprecated via the above mechanism: * Reader of type "npm"; Stage 1 from Version 1.0.8 on; see https://github.com/devonfw/solicitor/issues/62 * "REGEX:" prefix notation in rule templates (use "(REGEX)" suffix instead); Stage 1 from Version 1.3.0 on; see https://github.com/devonfw/solicitor/issues/78 * Use of `LicenseAssignmentProject.xls` decision table (use `LicenseAssignmentV2Project.xls` instead); Stage 1 from Version 1.4.0 on +* Use of `repoType` in the configuration of readers, see <>; Stage 1 from Version 1.14.0 on; see https://github.com/devonfw/solicitor/issues/190 == Experimental Scancode Integration @@ -1618,6 +1611,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.14.0:: * https://github.com/devonfw/solicitor/issues/202: Include parameter in CurationProvider.findCurations() to allow getting curation from alternative locations. +* https://github.com/devonfw/solicitor/issues/190: Deprecate `repoType` attribute in configuration of readers. Changes in 1.13.0:: * https://github.com/devonfw/solicitor/issues/198: Add PackageUrlHandler for nuget packages. From 0da8b68f7a3e504fbef14ccb9c1ca5aee1bac348 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 9 Oct 2023 10:14:34 +0200 Subject: [PATCH 070/139] Add specific exception indicating wrong curationDataSelector (#205) --- ...ExistingCurationDataSelectorException.java | 33 +++++++++++++++++++ .../curation/CurationProvider.java | 4 ++- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoAdapterNonExistingCurationDataSelectorException.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoAdapterNonExistingCurationDataSelectorException.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoAdapterNonExistingCurationDataSelectorException.java new file mode 100644 index 00000000..e285ba9d --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoAdapterNonExistingCurationDataSelectorException.java @@ -0,0 +1,33 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; + +/** + * This exception is thrown when an attempt is made to access a non-existing curation data selector when trying to + * retrieve curations. + * + */ +public class ComponentInfoAdapterNonExistingCurationDataSelectorException extends ComponentInfoAdapterException { + + /** + * Constructs a new ComponentInfoAdapterNonExistingCurationDataSelectorException with the specified detail message. + * + * @param message the detail message (which is saved for later retrieval by the getMessage() method). + */ + public ComponentInfoAdapterNonExistingCurationDataSelectorException(String message) { + + super(message); + } + + /** + * Constructs a new ComponentInfoAdapterNonExistingCurationDataSelectorException with the specified detail message and + * a nested Throwable cause. + * + * @param message the detail message (which is saved for later retrieval by the getMessage() method). + * @param cause the nested cause Throwable (which is saved for later retrieval by the getCause() method). + */ + public ComponentInfoAdapterNonExistingCurationDataSelectorException(String message, Throwable cause) { + + super(message, cause); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java index 0ef12c63..be9377ca 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java @@ -18,8 +18,10 @@ public interface CurationProvider { * indicates that the default should be used. * @return the curation data if it exists or null if no curation exist for the package. * @throws ComponentInfoAdapterException if something unexpected happens + * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException if the specified curationDataSelector could not be + * resolved */ ComponentInfoCuration findCurations(String packageUrl, String curationDataSelector) - throws ComponentInfoAdapterException; + throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException; } \ No newline at end of file From 2b9c0b8d556fe482cf50232172df741aab521dc0 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 9 Oct 2023 10:30:35 +0200 Subject: [PATCH 071/139] Version 1.14.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index b9c6c607..82249717 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.14.0-SNAPSHOT + 1.14.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 676d595a..37c2aeb0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.14.0-SNAPSHOT + 1.14.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index cf44e24d..0baf0ec0 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.14.0-SNAPSHOT + 1.14.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 43e6ed7a..24b7332b 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.14.0-SNAPSHOT + 1.14.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 4b97da58..70c2e241 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.14.0-SNAPSHOT + 1.14.0 pom Solicitor Aggregator From 6bfc85ca1babfbc7718212e3b61ec1df5ef82c91 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 9 Oct 2023 11:09:38 +0200 Subject: [PATCH 072/139] Set version to SNAPSHOT of next minor release --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 82249717..12bc8965 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.14.0 + 1.15.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 37c2aeb0..e6d77daa 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.14.0 + 1.15.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index a0c17446..16dbf29c 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1609,6 +1609,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.15.0:: + Changes in 1.14.0:: * https://github.com/devonfw/solicitor/issues/202: Include parameter in CurationProvider.findCurations() to allow getting curation from alternative locations. * https://github.com/devonfw/solicitor/issues/190: Deprecate `repoType` attribute in configuration of readers. diff --git a/documentation/pom.xml b/documentation/pom.xml index 0baf0ec0..d361b238 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.14.0 + 1.15.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 24b7332b..773fb84a 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.14.0 + 1.15.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 70c2e241..9e7c0c5c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.14.0 + 1.15.0-SNAPSHOT pom Solicitor Aggregator From eae33103a3110fb568863ec6060c8f1d59794008 Mon Sep 17 00:00:00 2001 From: duph97 Date: Tue, 24 Oct 2023 12:33:37 +0200 Subject: [PATCH 073/139] Feature/exclude paths in curation (#206) * First draft. Add excludedPaths property and inject ComponentInfoCurator into UncuratedScancodeComponentInfoProvider * New approach using SingleFileCurationProvider * Fix typo * Add Unit Tests * Add feature description and release note. * Fix NullPointerException * Revert scancode property to default false * Fix typo and add github issue to release note * Use CurationsProvider, Add curationDataSelector, Move exclusion logic from loop, Format code * Add Unit Test * Cleanup and Formatting * minor cleanup * Renamed interfaces/classes in from "Uncurated..." to "Filtered..." to align with changed semantics --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../curation/ComponentInfoCuratorImpl.java | 2 +- .../CuratingComponentInfoAdapter.java | 14 +- .../FilteredComponentInfoProvider.java | 14 ++ .../UncuratedComponentInfoProvider.java | 12 -- .../curation/model/ComponentInfoCuration.java | 18 +++ .../componentinfo_classes.drawio.svg | 2 +- .../FileScancodeRawComponentInfoProvider.java | 2 +- ...ilteredScancodeComponentInfoProvider.java} | 83 +++++++---- .../ScancodeComponentInfoAdapter.java | 6 +- ... => ScancodeRawComponentInfoProvider.java} | 2 +- ...redScancodeComponentInfoProviderTests.java | 129 ++++++++++++++++++ .../ScancodeComponentInfoAdapterTest.java | 17 ++- .../curations_with_exclusions.yaml | 10 ++ documentation/master-solicitor.asciidoc | 10 +- 14 files changed, 255 insertions(+), 66 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/FilteredComponentInfoProvider.java delete mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/UncuratedComponentInfoProvider.java rename core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/{UncuratedScancodeComponentInfoProvider.java => FilteredScancodeComponentInfoProvider.java} (78%) rename core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/{ScancodeRawComponentInfoPovider.java => ScancodeRawComponentInfoProvider.java} (95%) create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java create mode 100644 core/src/test/resources/scancodefileadapter/curations_with_exclusions.yaml diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java index b89ce6c5..a0b2e74d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java @@ -43,7 +43,7 @@ public ComponentInfoCuratorImpl(CurationProvider curationProvider, /** * Checks for the existence of curation for the given package via the {@link CurationProvider}. If curations exist - * then a new curated {@link ComponentInfo} instance will be created from the incoming uncurated {@link ComponentInfo} + * then a new curated {@link ComponentInfo} instance will be created from the incoming filtered {@link ComponentInfo} * and the curation. * * @param componentInfo the componentInfo to curate diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java index 4061aa54..ceafa745 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java @@ -5,26 +5,26 @@ import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; /** - * A {@link ComponentInfoAdapter} which takes uncurated {@link ComponentInfo} data from the configuret - * {@link UncuratedComponentInfoProvider} and curates it via the given {@link ComponentInfoCurator}. + * A {@link ComponentInfoAdapter} which takes filtered {@link ComponentInfo} data from the configuret + * {@link FilteredComponentInfoProvider} and curates it via the given {@link ComponentInfoCurator}. * */ public class CuratingComponentInfoAdapter implements ComponentInfoAdapter { - private UncuratedComponentInfoProvider uncuratedComponentInfoProvider; + private FilteredComponentInfoProvider filteredComponentInfoProvider; private ComponentInfoCurator componentInfoCurator; /** * The constructor. * - * @param uncuratedComponentInfoProvider the provider of the uncurated {@link ComponentInfo} data + * @param filteredComponentInfoProvider the provider of the filtered {@link ComponentInfo} data * @param componentInfoCurator the curator to take */ - public CuratingComponentInfoAdapter(UncuratedComponentInfoProvider uncuratedComponentInfoProvider, + public CuratingComponentInfoAdapter(FilteredComponentInfoProvider filteredComponentInfoProvider, ComponentInfoCurator componentInfoCurator) { - this.uncuratedComponentInfoProvider = uncuratedComponentInfoProvider; + this.filteredComponentInfoProvider = filteredComponentInfoProvider; this.componentInfoCurator = componentInfoCurator; } @@ -46,7 +46,7 @@ public ComponentInfo getComponentInfo(String packageUrl, String curationDataSele if (isFeatureActive()) { - ComponentInfo componentInfo = this.uncuratedComponentInfoProvider.getComponentInfo(packageUrl, + ComponentInfo componentInfo = this.filteredComponentInfoProvider.getComponentInfo(packageUrl, curationDataSelector); if (componentInfo == null) { return null; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/FilteredComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/FilteredComponentInfoProvider.java new file mode 100644 index 00000000..08bc4a3a --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/FilteredComponentInfoProvider.java @@ -0,0 +1,14 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoProvider; + +/** + * A {@link ComponentInfoProvider} which provides filtered {@link ComponentInfo}s. This is {@link ComponentInfo} which + * is not yet fully curated but data is already filtered to remove information which applies to portions of the original + * scanned code which should be disregarded. + * + */ +public interface FilteredComponentInfoProvider extends ComponentInfoProvider { + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/UncuratedComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/UncuratedComponentInfoProvider.java deleted file mode 100644 index 3c0bf998..00000000 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/UncuratedComponentInfoProvider.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.devonfw.tools.solicitor.componentinfo.curation; - -import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; -import com.devonfw.tools.solicitor.componentinfo.ComponentInfoProvider; - -/** - * A {@link ComponentInfoProvider} which provides uncurated {@link ComponentInfo}s. - * - */ -public interface UncuratedComponentInfoProvider extends ComponentInfoProvider { - -} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java index bd84c95f..94e85453 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java @@ -21,6 +21,8 @@ public class ComponentInfoCuration { private List licenses; + private List excludedPaths; + /** * The constructor. */ @@ -108,4 +110,20 @@ public void setLicenses(List licenses) { this.licenses = licenses; } + /** + * @return excluded paths + */ + public List getExcludedPaths() { + + return this.excludedPaths; + } + + /** + * @param excludedPaths new value of {@link #getExcludedPaths}. + */ + public void setExcludedPaths(List excludedPaths) { + + this.excludedPaths = excludedPaths; + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/doc-files/componentinfo_classes.drawio.svg b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/doc-files/componentinfo_classes.drawio.svg index 07283864..1408ed23 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/doc-files/componentinfo_classes.drawio.svg +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/doc-files/componentinfo_classes.drawio.svg @@ -1,3 +1,3 @@ -
        Curation data
        Curation data
        ComponentInfo data
        ComponentInfo da...
        «interface»
        ComponentInfo
        «interface»...
        «interface»
        LicenseInfo
        «interface»...
        DefaultComponentInfoImpl
        DefaultComponentInfoImpl
        DefaultLicenseInfoImpl
        DefaultLicenseInfoImpl

        <<Interface>>
        ComponentInfoProvider


        + getComponentInfo(packageUrl): ComponentInfo


        <<Interface>>...
        «interface»
        ComponentInfoAdapter
        «interface»...
        Extends
        Extends
        use
        use


        ComponentInfoInventoryProcessor

        ComponentInfoInventoryProcessor
        «interface»
        UncuratedComponentInfoProvider
        «interface»...
        Extends
        Extends


        CuratingComponentInfoAdapter

        CuratingComponentInfoAdapter

        <<Interface>>
        ComponentInfoCurator


        + curate(componentInfo): ComponentInfo

        <<Interface>>...
        Use
        Use
        Use
        Use


        ComponentInfoCuratorImpl

        ComponentInfoCuratorImpl

        <<Interface>>
        CurationProvider


        + findCuration(packageUrl): ComponentInfoCuration

        <<Interface>>...
        Use
        Use


        SingleFileCurationProvider

        SingleFileCurationProvider


        ComponentInfoCuration

        ComponentInfoCuration


        LicenseInfoCuration

        LicenseInfoCuration


        ScancodeComponentInfoAdapter

        ScancodeComponentInfoAdapter
        Extends
        Extends


        UncuratedScancodeComponentInfoProvider

        UncuratedScancodeComponentInfoProvider
        Use
        Use

        <<Interface>>
        ScancodeRawComponentInfoProvider


        + readScancodeData(packageUrl): ScancodeRawComponentInfo

        + pkgContentUriFromPath(packageUrl, path): String

        + isLocalContentPath(packageUrl, path): boolean

        <<Interface>>...


        FileScancodeRawComponentInfoProvider

        FileScancodeRawComponentInfoProvider...
        ScancodeComponentInfo
        ScancodeComponentInfo
        ScancodeLicenseInfo
        ScancodeLicenseInfo
        Package:
        com.devonfw.tools.solictor.componentinfo.scancode
        Package:com.devonfw.tools.solictor.componentinfo.sc...
        Package:
        com.devonfw.tools.solictor.componentinfo
        Package:com.devonfw.tools.solictor.compo...
        Package:
        com.devonfw.tools.solictor.componentinfo.curation(.model)
        Package:com.devonfw.tools.solictor.componentinfo.curatio...
        Classes with thick line are instantiated as beans
        Classes with thick line are instantiated as b...

        <<Interface>>
        ComponentContentProvider


        + retrieveContent(packageUrl, path) : String

        <<Interface>>...
        Extends
        Extends
        Use
        Use
        Viewer does not support full SVG 1.1
        \ No newline at end of file +
        Curation data
        Curation data
        ComponentInfo data
        ComponentInfo da...
        «interface»
        ComponentInfo
        «interface»...
        «interface»
        LicenseInfo
        «interface»...
        DefaultComponentInfoImpl
        DefaultComponentInfoImpl
        DefaultLicenseInfoImpl
        DefaultLicenseInfoImpl

        <<Interface>>
        ComponentInfoProvider


        + getComponentInfo(packageUrl): ComponentInfo


        <<Interface>>...
        «interface»
        ComponentInfoAdapter
        «interface»...
        Extends
        Extends
        use
        use


        ComponentInfoInventoryProcessor

        ComponentInfoInventoryProcessor
        «interface»
        FilteredComponentInfoProvider
        «interface»...
        Extends
        Extends


        CuratingComponentInfoAdapter

        CuratingComponentInfoAdapter

        <<Interface>>
        ComponentInfoCurator


        + curate(componentInfo): ComponentInfo

        <<Interface>>...
        Use
        Use
        Use
        Use


        ComponentInfoCuratorImpl

        ComponentInfoCuratorImpl

        <<Interface>>
        CurationProvider


        + findCuration(packageUrl): ComponentInfoCuration

        <<Interface>>...
        Use
        Use


        SingleFileCurationProvider

        SingleFileCurationProvider


        ComponentInfoCuration

        ComponentInfoCuration


        LicenseInfoCuration

        LicenseInfoCuration


        ScancodeComponentInfoAdapter

        ScancodeComponentInfoAdapter
        Extends
        Extends


        FilteredScancodeComponentInfoProvider

        FilteredScancodeComponentInfoProvider
        Use
        Use

        <<Interface>>
        ScancodeRawComponentInfoProvider


        + readScancodeData(packageUrl): ScancodeRawComponentInfo

        + pkgContentUriFromPath(packageUrl, path): String

        + isLocalContentPath(packageUrl, path): boolean

        <<Interface>>...


        FileScancodeRawComponentInfoProvider

        FileScancodeRawComponentInfoProvider...
        ScancodeComponentInfo
        ScancodeComponentInfo
        ScancodeLicenseInfo
        ScancodeLicenseInfo
        Package:
        com.devonfw.tools.solictor.componentinfo.scancode
        Package:com.devonfw.tools.solictor.componentinfo.sc...
        Package:
        com.devonfw.tools.solictor.componentinfo
        Package:com.devonfw.tools.solictor.compo...
        Package:
        com.devonfw.tools.solictor.componentinfo.curation(.model)
        Package:com.devonfw.tools.solictor.componentinfo.curatio...
        Classes with thick line are instantiated as beans
        Classes with thick line are instantiated as b...

        <<Interface>>
        ComponentContentProvider


        + retrieveContent(packageUrl, path) : String

        <<Interface>>...
        Extends
        Extends
        Use
        Use
        Use
        Use
        Viewer does not support full SVG 1.1
        \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java index dcd7fd79..75ad03c9 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java @@ -24,7 +24,7 @@ * */ @Component -public class FileScancodeRawComponentInfoProvider implements ScancodeRawComponentInfoPovider { +public class FileScancodeRawComponentInfoProvider implements ScancodeRawComponentInfoProvider { /** * The directory within the component root directory which contains the sources / the content diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java similarity index 78% rename from core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java rename to core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java index c4a95aba..59017573 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/UncuratedScancodeComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java @@ -1,6 +1,7 @@ package com.devonfw.tools.solicitor.componentinfo.scancode; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.slf4j.Logger; @@ -12,7 +13,9 @@ import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; -import com.devonfw.tools.solicitor.componentinfo.curation.UncuratedComponentInfoProvider; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationProvider; +import com.devonfw.tools.solicitor.componentinfo.curation.FilteredComponentInfoProvider; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -20,18 +23,16 @@ import com.github.packageurl.PackageURL; /** - * {@link UncuratedComponentInfoProvider} which delivers data based on scancode data. + * {@link FilteredComponentInfoProvider} which delivers data based on scancode data. * */ @Component -public class UncuratedScancodeComponentInfoProvider implements UncuratedComponentInfoProvider { +public class FilteredScancodeComponentInfoProvider implements FilteredComponentInfoProvider { - private static final Logger LOG = LoggerFactory.getLogger(UncuratedScancodeComponentInfoProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(FilteredScancodeComponentInfoProvider.class); private static final ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); - private String repoBasePath; - private double minLicenseScore; private int minLicensefileNumberOfLines; @@ -40,34 +41,28 @@ public class UncuratedScancodeComponentInfoProvider implements UncuratedComponen private AllKindsPackageURLHandler packageURLHandler; - private ScancodeRawComponentInfoPovider fileScancodeRawComponentInfoProvider; + private ScancodeRawComponentInfoProvider fileScancodeRawComponentInfoProvider; + + private CurationProvider curationProvider; /** * The constructor. * * @param fileScancodeRawComponentInfoProvider the provide for the raw scancode data * @param packageURLHandler the handler for dealing with {@link PackageURL}s. + * @param curationProvider for getting the filter information used for filtering findings based on the paths in the + * code */ @Autowired - public UncuratedScancodeComponentInfoProvider(ScancodeRawComponentInfoPovider fileScancodeRawComponentInfoProvider, - AllKindsPackageURLHandler packageURLHandler) { + public FilteredScancodeComponentInfoProvider(ScancodeRawComponentInfoProvider fileScancodeRawComponentInfoProvider, + AllKindsPackageURLHandler packageURLHandler, CurationProvider curationProvider) { this.fileScancodeRawComponentInfoProvider = fileScancodeRawComponentInfoProvider; this.packageURLHandler = packageURLHandler; + this.curationProvider = curationProvider; } - /** - * Sets repoBasePath. - * - * @param repoBasePath new value of repoBasePath. - */ - @Value("${solicitor.scancode.repo-base-path}") - public void setRepoBasePath(String repoBasePath) { - - this.repoBasePath = repoBasePath; - } - /** * Sets minLicenseScore. * @@ -109,7 +104,8 @@ public ScancodeComponentInfo getComponentInfo(String packageUrl, String curation return null; } - ScancodeComponentInfo componentScancodeInfos = parseAndMapScancodeJson(packageUrl, rawScancodeData); + ScancodeComponentInfo componentScancodeInfos = parseAndMapScancodeJson(packageUrl, rawScancodeData, + curationDataSelector); addSupplementedData(rawScancodeData, componentScancodeInfos); LOG.debug("Scancode info for package {}: {} license, {} copyrights, {} NOTICE files", packageUrl, componentScancodeInfos.getLicenses().size(), componentScancodeInfos.getCopyrights().size(), @@ -135,13 +131,22 @@ private void addSupplementedData(ScancodeRawComponentInfo rawScancodeData, * @return * @throws ComponentInfoAdapterException */ - private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, ScancodeRawComponentInfo rawScancodeData) - throws ComponentInfoAdapterException { + private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, ScancodeRawComponentInfo rawScancodeData, + String curationDataSelector) throws ComponentInfoAdapterException { ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore, this.minLicensefileNumberOfLines); componentScancodeInfos.setPackageUrl(packageUrl); + // Get the curation for a given packageUrl + ComponentInfoCuration componentInfoCuration = this.curationProvider.findCurations(packageUrl, curationDataSelector); + + // Get all excludedPaths in this curation + List excludedPaths = null; + if (componentInfoCuration != null) { + excludedPaths = componentInfoCuration.getExcludedPaths(); + } + JsonNode scancodeJson; try { scancodeJson = mapper.readTree(rawScancodeData.rawScancodeResult); @@ -149,14 +154,18 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod throw new ComponentInfoAdapterException("Could not parse Scancode JSON", e); } + // Skip all files, whose path have a prefix which is in the excluded path list for (JsonNode file : scancodeJson.get("files")) { + String path = file.get("path").asText(); + if (isExcluded(path, excludedPaths)) { + continue; + } if ("directory".equals(file.get("type").asText())) { continue; } - if (file.get("path").asText().contains("/NOTICE")) { - componentScancodeInfos.addNoticeFileUrl( - this.fileScancodeRawComponentInfoProvider.pkgContentUriFromPath(packageUrl, file.get("path").asText()), - 100.0); + if (path.contains("/NOTICE")) { + componentScancodeInfos + .addNoticeFileUrl(this.fileScancodeRawComponentInfoProvider.pkgContentUriFromPath(packageUrl, path), 100.0); } double licenseTextRatio = file.get("percentage_of_license_text").asDouble(); boolean takeCompleteFile = licenseTextRatio >= this.licenseToTextRatioToTakeCompleteFile; @@ -216,7 +225,7 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod String licenseDefaultUrl = li.get("scancode_text_url").asText(); licenseDefaultUrl = normalizeLicenseUrl(packageUrl, licenseDefaultUrl); double score = li.get("score").asDouble(); - String licenseUrl = file.get("path").asText(); + String licenseUrl = path; int startLine = li.get("start_line").asInt(); int endLine = li.get("end_line").asInt(); if (!takeCompleteFile) { @@ -267,4 +276,22 @@ private String normalizeLicenseUrl(String packageUrl, String licenseUrl) { return adjustedLicenseUrl; } + /** + * Check if the given path prefix is excluded in the curation. + * + * @param path in the scancode data + * @param excludedPaths all excluded paths defined in the curation + * @return true if path prefix is excluded in curation + */ + private boolean isExcluded(String path, List excludedPaths) { + + if (excludedPaths != null) { + for (String excludedPath : excludedPaths) { + if (path.startsWith(excludedPath)) { + return true; + } + } + } + return false; + } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapter.java index 92cebae3..151acd8c 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapter.java @@ -29,14 +29,14 @@ public class ScancodeComponentInfoAdapter extends CuratingComponentInfoAdapter { /** * The constructor. * - * @param uncuratedScancodeComponentInfoProvider provider for uncurated data originating from scancode data + * @param filteredScancodeComponentInfoProvider provider for filtered data originating from scancode data * @param componentInfoCurator the curator to use */ @Autowired - public ScancodeComponentInfoAdapter(UncuratedScancodeComponentInfoProvider uncuratedScancodeComponentInfoProvider, + public ScancodeComponentInfoAdapter(FilteredScancodeComponentInfoProvider filteredScancodeComponentInfoProvider, ComponentInfoCurator componentInfoCurator) { - super(uncuratedScancodeComponentInfoProvider, componentInfoCurator); + super(filteredScancodeComponentInfoProvider, componentInfoCurator); } /** diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoPovider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoProvider.java similarity index 95% rename from core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoPovider.java rename to core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoProvider.java index 3bfc8ad6..46725130 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoPovider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoProvider.java @@ -7,7 +7,7 @@ * Provider for {@link ScancodeRawComponentInfo} * */ -public interface ScancodeRawComponentInfoPovider extends ComponentContentProvider { +public interface ScancodeRawComponentInfoProvider extends ComponentContentProvider { /** * Retrieve the {@link ScancodeRawComponentInfo} for the package given by its PackageURL. diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java new file mode 100644 index 00000000..59fdb2a5 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java @@ -0,0 +1,129 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; + +/** + * This class contains JUnit test methods for the {@link FilteredScancodeComponentInfoProvider} class. + */ +public class FilteredScancodeComponentInfoProviderTests { + + // the object under test + FilteredScancodeComponentInfoProvider filteredScancodeComponentInfoProvider; + + FileScancodeRawComponentInfoProvider fileScancodeRawComponentInfoProvider; + + SingleFileCurationProvider singleFileCurationProvider; + + @BeforeEach + public void setup() { + + AllKindsPackageURLHandler packageURLHandler = Mockito.mock(AllKindsPackageURLHandler.class); + + Mockito.when(packageURLHandler.pathFor("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0")) + .thenReturn("pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0"); + DirectUrlWebContentProvider contentProvider = new DirectUrlWebContentProvider(false); + + this.fileScancodeRawComponentInfoProvider = new FileScancodeRawComponentInfoProvider(contentProvider, + packageURLHandler); + this.fileScancodeRawComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); + + this.singleFileCurationProvider = new SingleFileCurationProvider(packageURLHandler); + this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/curations.yaml"); + + this.filteredScancodeComponentInfoProvider = new FilteredScancodeComponentInfoProvider( + this.fileScancodeRawComponentInfoProvider, packageURLHandler, this.singleFileCurationProvider); + + } + + /** + * Test the {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String,String)} method when no curations + * file exists + * + * @throws ComponentInfoAdapterException if something goes wrong + */ + @Test + public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterException { + + // given + this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/nonexisting.yaml"); + + // when + ScancodeComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + + // then + assertNotNull(scancodeComponentInfo); + assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + scancodeComponentInfo.getPackageUrl()); + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", + scancodeComponentInfo.getNoticeFileContent()); + assertEquals(1, scancodeComponentInfo.getCopyrights().size()); + assertEquals("Copyright 2023 devonfw", scancodeComponentInfo.getCopyrights().toArray()[0]); + } + + /** + * Test the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} method when the /src directory is + * excluded + * + * @throws ComponentInfoAdapterException if something goes wrong + */ + @Test + public void testGetComponentInfoWithCurationsAndExclusions() throws ComponentInfoAdapterException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/curations_with_exclusions.yaml"); + // when + ScancodeComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + + // then + assertNotNull(scancodeComponentInfo); + assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + scancodeComponentInfo.getPackageUrl()); + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", + scancodeComponentInfo.getNoticeFileContent()); + assertEquals(0, scancodeComponentInfo.getCopyrights().size()); // since the copyright is found under + // /src/../SampleClass.java1, it will be excluded + } + + /** + * Test the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} method when curations exist but no + * paths are excluded + * + * @throws ComponentInfoAdapterException if something goes wrong + */ + @Test + public void testGetComponentInfoWithCurationsAndWithoutExclusions() throws ComponentInfoAdapterException { + + // given + this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/curations.yaml"); + + // when + ScancodeComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + + // then + assertNotNull(scancodeComponentInfo); + assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + scancodeComponentInfo.getPackageUrl()); + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", + scancodeComponentInfo.getNoticeFileContent()); + assertEquals(1, scancodeComponentInfo.getCopyrights().size()); + assertEquals("Copyright 2023 devonfw", scancodeComponentInfo.getCopyrights().toArray()[0]); // The copyright + // curation does not + // apply on the + // scancodeComponentInfo + // object. + } +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java index 4d30fdcf..a090f35e 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java @@ -31,7 +31,7 @@ class ScancodeComponentInfoAdapterTest { // the object under test ScancodeComponentInfoAdapter scancodeComponentInfoAdapter; - UncuratedScancodeComponentInfoProvider uncuratedScancodeComponentInfoProvider; + FilteredScancodeComponentInfoProvider filteredScancodeComponentInfoProvider; FileScancodeRawComponentInfoProvider fileScancodeRawComponentInfoProvider; @@ -52,19 +52,18 @@ public void setup() { packageURLHandler); this.fileScancodeRawComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); - this.uncuratedScancodeComponentInfoProvider = new UncuratedScancodeComponentInfoProvider( - this.fileScancodeRawComponentInfoProvider, packageURLHandler); - this.uncuratedScancodeComponentInfoProvider.setMinLicensefileNumberOfLines(5); - this.uncuratedScancodeComponentInfoProvider.setMinLicenseScore(90.0); - this.uncuratedScancodeComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); - this.singleFileCurationProvider = new SingleFileCurationProvider(packageURLHandler); this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/curations.yaml"); + this.filteredScancodeComponentInfoProvider = new FilteredScancodeComponentInfoProvider( + this.fileScancodeRawComponentInfoProvider, packageURLHandler, this.singleFileCurationProvider); + this.filteredScancodeComponentInfoProvider.setMinLicensefileNumberOfLines(5); + this.filteredScancodeComponentInfoProvider.setMinLicenseScore(90.0); + this.componentInfoCuratorImpl = new ComponentInfoCuratorImpl(this.singleFileCurationProvider, this.fileScancodeRawComponentInfoProvider); - this.scancodeComponentInfoAdapter = new ScancodeComponentInfoAdapter(this.uncuratedScancodeComponentInfoProvider, + this.scancodeComponentInfoAdapter = new ScancodeComponentInfoAdapter(this.filteredScancodeComponentInfoProvider, this.componentInfoCuratorImpl); this.scancodeComponentInfoAdapter.setFeatureFlag(true); @@ -152,7 +151,7 @@ public void testGetComponentCheckCurationDataSelector() throws ComponentInfoAdap this.componentInfoCuratorImpl = new ComponentInfoCuratorImpl(curationProvider, this.fileScancodeRawComponentInfoProvider); - this.scancodeComponentInfoAdapter = new ScancodeComponentInfoAdapter(this.uncuratedScancodeComponentInfoProvider, + this.scancodeComponentInfoAdapter = new ScancodeComponentInfoAdapter(this.filteredScancodeComponentInfoProvider, this.componentInfoCuratorImpl); this.scancodeComponentInfoAdapter.setFeatureFlag(true); diff --git a/core/src/test/resources/scancodefileadapter/curations_with_exclusions.yaml b/core/src/test/resources/scancodefileadapter/curations_with_exclusions.yaml new file mode 100644 index 00000000..3290c21a --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/curations_with_exclusions.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + url: "http://some/url" + licenses: + - license: MIT + url: https://some/license/url + excludedPaths: + - "sources/src" + - "foo" \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 16dbf29c..4cf1903e 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1439,7 +1439,9 @@ artifacts: copyrights: <6> - (c) 2021 Donald Duck <7> - "(c) 2019 Mickey Mouse " <8> - - name: pkg/npm/@anotherscope/anotherpackage/4.5.6 <9> + excludedPaths: <9> + - "sources/src" <10> + - name: pkg/npm/@anotherscope/anotherpackage/4.5.6 <11> . . . @@ -1452,7 +1454,9 @@ artifacts: <6> Copyrights to set. Optional. If defined then all found copyrights will be replaced by the list of copyrights given here. <7> A single copyright. <8> Another copyright. Note that due to YAML syntax any string containing `:` needs to be enclosed with parentheses -<9> Further packages to follow. +<9> Excluded paths to be set. Optional. If defined then all scanned files, whose path prefix contain any given string here, are excluded from the ScanCode information. +<10> A single path prefix. All scanned files starting with this path prefix are excluded from the Scancode information. +<11> Further packages to follow. ==== Decision table rules As for license information obtained from the Readers the license information from ScanCode can also be altered using decision table rules. A new attribute `origin` was introduced in the `RawLicense` entity as well as condition field in decision table `LicenseAssignmentV2*.xls/csv`. The `origin` attribute in `Rawlicense` either contains the string `scancode` if the license information came from ScanCode or it contains the (lowercase) class name of the used Reader. @@ -1610,7 +1614,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.15.0:: - +* https://github.com/devonfw/solicitor/issues/207: Add a new feature allowing the exclusion of paths and files of scanned artifacts from the Scancode information. Changes in 1.14.0:: * https://github.com/devonfw/solicitor/issues/202: Include parameter in CurationProvider.findCurations() to allow getting curation from alternative locations. * https://github.com/devonfw/solicitor/issues/190: Deprecate `repoType` attribute in configuration of readers. From 9a2c24bdfd98b2c3b3151b4bac3b142264dcf73d Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 24 Oct 2023 14:24:54 +0200 Subject: [PATCH 074/139] corrected javadoc --- .../tools/solicitor/componentinfo/curation/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/package-info.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/package-info.java index 263c1d2d..68bf2cb2 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/package-info.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/package-info.java @@ -1,5 +1,5 @@ /** - * Interfaces and classes related to applying curation data to the uncurated + * Interfaces and classes related to applying curation data to the filtered * {@link com.devonfw.tools.solicitor.componentinfo.ComponentInfo} data. *

        * See {@link com.devonfw.tools.solicitor.componentinfo} for an overview diagram of interfaces/classes. From f92a607eff01638f7982e92fb9e1eb54cceadf1e Mon Sep 17 00:00:00 2001 From: duph97 Date: Thu, 26 Oct 2023 14:20:19 +0200 Subject: [PATCH 075/139] Additional method in SolicitorLifecycleListener (#209) * Add LifeCycleListener method to to be applied before writing reports * Add release note * moved method call with the lifecycle and renamed it accordingly * added another method "afterReadingInventory" which might be usefull as an extension point --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../devonfw/tools/solicitor/Solicitor.java | 2 ++ .../AbstractSolicitorLifecycleListener.java | 12 +++++++ .../lifecycle/LifecycleListenerHolder.java | 33 +++++++++++++++++++ .../lifecycle/SolicitorLifecycleListener.java | 18 ++++++++++ documentation/master-solicitor.asciidoc | 1 + 5 files changed, 66 insertions(+) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java index e7090bdb..2acfc574 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java @@ -118,8 +118,10 @@ private void mainProcessing(CommandLineOptions clo) { modelRoot = this.modelImporterExporter.loadModel(clo.pathForLoad); } else { readInventory(); + this.lifecycleListenerHolder.afterReadingInventory(modelRoot); runInventoryProcessors(modelRoot); modelRoot.completeData(); + this.lifecycleListenerHolder.afterModelProcessing(modelRoot); } if (clo.save) { LOG.info(LogMessages.SAVING_DATAMODEL.msg(), clo.pathForSave); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/AbstractSolicitorLifecycleListener.java b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/AbstractSolicitorLifecycleListener.java index 6bf7cf55..42b110b7 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/AbstractSolicitorLifecycleListener.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/AbstractSolicitorLifecycleListener.java @@ -22,6 +22,18 @@ public void modelRootInitialized(ModelRoot modelRoot) { // NOOP by default } + @Override + public void afterReadingInventory(ModelRoot modelRoot) { + + // NOOP by default + } + + @Override + public void afterModelProcessing(ModelRoot modelRoot) { + + // NOOP by default + } + @Override public void endOfMainProcessing(ModelRoot modelRoot) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/LifecycleListenerHolder.java b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/LifecycleListenerHolder.java index 2803c0d3..df7dfd78 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/LifecycleListenerHolder.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/LifecycleListenerHolder.java @@ -37,6 +37,39 @@ public void modelRootInitialized(ModelRoot modelRoot) { } } + /** + * Method to be called after the inventory was read (via the readers) but before any further processing. + * + * @param modelRoot the model data as existing after the inventory was read via the readers. + */ + public void afterReadingInventory(ModelRoot modelRoot) { + + if (this.listeners == null) { + return; + } + for (SolicitorLifecycleListener sll : this.listeners) { + sll.afterReadingInventory(modelRoot); + } + + } + + /** + * Calls {@link SolicitorLifecycleListener#afterModelProcessing(ModelRoot)} of all registered + * {@link SolicitorLifecycleListener}s. + * + * @param modelRoot modelRoot the model data as existing after the model processing but before model is (optionally) + * saved or writers are called + */ + public void afterModelProcessing(ModelRoot modelRoot) { + + if (this.listeners == null) { + return; + } + for (SolicitorLifecycleListener sll : this.listeners) { + sll.afterModelProcessing(modelRoot); + } + } + /** * Calls {@link SolicitorLifecycleListener#endOfMainProcessing(ModelRoot)} of all registered * {@link SolicitorLifecycleListener}s. diff --git a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/SolicitorLifecycleListener.java b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/SolicitorLifecycleListener.java index 0e98718b..710d5c6c 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/SolicitorLifecycleListener.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/lifecycle/SolicitorLifecycleListener.java @@ -1,5 +1,6 @@ package com.devonfw.tools.solicitor.lifecycle; +import com.devonfw.tools.solicitor.InventoryProcessor; import com.devonfw.tools.solicitor.model.ModelRoot; /** @@ -15,6 +16,23 @@ public interface SolicitorLifecycleListener { */ void modelRootInitialized(ModelRoot modelRoot); + /** + * Method to be called after the inventory was read (via the readers) but before any further processing. + * + * @param modelRoot the model data as existing after the inventory was read via the readers. + */ + void afterReadingInventory(ModelRoot modelRoot); + + /** + * Method to be called after the model has been processed, but before the model is potentially saved (and the writers + * are called). + * + * @param modelRoot the model data as existing after the model processing (running {@link InventoryProcessor}s and + * completing data). This is before (optionally) saving the model and running the writers to generate the + * reports. + */ + void afterModelProcessing(ModelRoot modelRoot); + /** * Method to be called at the end of main processing. * diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 4cf1903e..cb7fdef5 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1614,6 +1614,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.15.0:: +* https://github.com/devonfw/solicitor/issues/208: Add two new lifecycle methods to `SolicitorLifecycleListener`. * https://github.com/devonfw/solicitor/issues/207: Add a new feature allowing the exclusion of paths and files of scanned artifacts from the Scancode information. Changes in 1.14.0:: * https://github.com/devonfw/solicitor/issues/202: Include parameter in CurationProvider.findCurations() to allow getting curation from alternative locations. From 4f5b497203d6b0b88bb8c64389731de1551d22a2 Mon Sep 17 00:00:00 2001 From: malkam <108339027+mahmoudAlkam@users.noreply.github.com> Date: Sat, 28 Oct 2023 16:40:13 +0200 Subject: [PATCH 076/139] Feature/non default curation data selector (#210) * using curation data from non default curationDataSelector * using curation data from non default curationDataSelector * Check curationDataSelector is empty and then set "null". * Review and documentation --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../ComponentInfoInventoryProcessor.java | 22 ++++++++++++++++++- .../src/main/resources/application.properties | 5 +++++ documentation/files/application.properties | 5 +++++ documentation/master-solicitor.asciidoc | 2 ++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 2f40a84f..04fb064c 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @@ -51,6 +52,25 @@ public void add(Statistics statistics) { private ModelFactory modelFactory; + private String curationDataSelector; + + /** + * Set the curationDataSelector. + * + * @param curationDataSelector the curationDataSelector to chose when getting curation data. An empty string will be + * stored as null null which results in the default origin being taken. + */ + @Value("${solicitor.curationDataSelector}") + public void setCurationDataSelector(String curationDataSelector) { + + if (curationDataSelector != null && curationDataSelector.isEmpty()) { + this.curationDataSelector = null; + } else { + this.curationDataSelector = curationDataSelector; + + } + } + /** * The constructor. */ @@ -91,7 +111,7 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { ComponentInfo componentInfo = null; try { for (ComponentInfoProvider cia : this.componentInfoAdapters) { - componentInfo = cia.getComponentInfo(ac.getPackageUrl(), null); + componentInfo = cia.getComponentInfo(ac.getPackageUrl(), this.curationDataSelector); // stop querying further adapters if some info was returned if (componentInfo != null) { break; diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index 746a22cf..b81ecb0d 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -57,6 +57,11 @@ solicitor.scancode.curations-filename=output/curations.yaml # base path of the file repo where sources and scancode information is stored solicitor.scancode.repo-base-path=output/Source +# The curationDataSelector value to use when accessing curation data. +# You can change its value to select a specific curation data source (if the implementation supports this). +# Leave it empty to use the default curation data source. +solicitor.curationDataSelector= + # If there is an exception while reading the raw inventory data for an application the processing will be aborted by default. # By setting this property to true processing will continue in case that the input file is not found. This allows processing # in multi application projects where some input files are not yet available. A warning (SOLI-045) will be logged in this case and diff --git a/documentation/files/application.properties b/documentation/files/application.properties index 746a22cf..b81ecb0d 100644 --- a/documentation/files/application.properties +++ b/documentation/files/application.properties @@ -57,6 +57,11 @@ solicitor.scancode.curations-filename=output/curations.yaml # base path of the file repo where sources and scancode information is stored solicitor.scancode.repo-base-path=output/Source +# The curationDataSelector value to use when accessing curation data. +# You can change its value to select a specific curation data source (if the implementation supports this). +# Leave it empty to use the default curation data source. +solicitor.curationDataSelector= + # If there is an exception while reading the raw inventory data for an application the processing will be aborted by default. # By setting this property to true processing will continue in case that the input file is not found. This allows processing # in multi application projects where some input files are not yet available. A warning (SOLI-045) will be logged in this case and diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index cb7fdef5..843aaa7e 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1616,6 +1616,8 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.15.0:: * https://github.com/devonfw/solicitor/issues/208: Add two new lifecycle methods to `SolicitorLifecycleListener`. * https://github.com/devonfw/solicitor/issues/207: Add a new feature allowing the exclusion of paths and files of scanned artifacts from the Scancode information. +* https://github.com/devonfw/solicitor/issues/211: Allow setting alternative locations for curation data (`curationDataSelector`). Note that the standard implementation included within Solicitor does not yet honor this value. + Changes in 1.14.0:: * https://github.com/devonfw/solicitor/issues/202: Include parameter in CurationProvider.findCurations() to allow getting curation from alternative locations. * https://github.com/devonfw/solicitor/issues/190: Deprecate `repoType` attribute in configuration of readers. From 40d6c5c7ca102b65341bb496f6e0be7c5c020be1 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Sat, 28 Oct 2023 17:00:17 +0200 Subject: [PATCH 077/139] Version 1.15.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 12bc8965..a388e541 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.15.0-SNAPSHOT + 1.15.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index e6d77daa..e98f292c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.15.0-SNAPSHOT + 1.15.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index d361b238..2b93c1df 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.15.0-SNAPSHOT + 1.15.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 773fb84a..5443a085 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.15.0-SNAPSHOT + 1.15.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 9e7c0c5c..5d145edb 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.15.0-SNAPSHOT + 1.15.0 pom Solicitor Aggregator From 9a5aa0b1ca3d62a089e40487a4827dd5458cf62a Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Sat, 28 Oct 2023 17:10:38 +0200 Subject: [PATCH 078/139] Set version to SNAPSHOT of next minor release --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index a388e541..d3a9058b 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.15.0 + 1.16.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index e98f292c..b7ed8275 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.15.0 + 1.16.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 843aaa7e..b9a5eadc 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1613,6 +1613,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.16.0:: + Changes in 1.15.0:: * https://github.com/devonfw/solicitor/issues/208: Add two new lifecycle methods to `SolicitorLifecycleListener`. * https://github.com/devonfw/solicitor/issues/207: Add a new feature allowing the exclusion of paths and files of scanned artifacts from the Scancode information. diff --git a/documentation/pom.xml b/documentation/pom.xml index 2b93c1df..f3a9c34b 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.15.0 + 1.16.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 5443a085..2b2ce88a 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.15.0 + 1.16.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 5d145edb..bd4d94ab 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.15.0 + 1.16.0-SNAPSHOT pom Solicitor Aggregator From ff07997eeaf5a01a91a754b400493d28798151e8 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 3 Nov 2023 15:01:56 +0100 Subject: [PATCH 079/139] Improvement in determining License-URL within NpmLicenseCheckerReader (#212) * Improvement in determining License-URL within NpmLicenseCheckerReader * Updated release notes in user guide --- .../npmlicensechecker/NpmLicenseCheckerReader.java | 10 +++++++++- .../NpmLicenseCheckerReaderTests.java | 4 ++-- documentation/master-solicitor.asciidoc | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java index c5cc002d..df6ad64e 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java @@ -132,7 +132,15 @@ private String estimateLicenseUrl(String repo, String path, String licenseFile) repo = repo.substring(0, repo.length() - 1); } if (repo.contains("github.com")) { - return repo.replace("git://", "https://") + "/raw/master" + licenseRelative; + if (repo.contains("/tree")) { + return repo.replace("git://", "https://").replace("github.com", "raw.githubusercontent.com").replace("/tree", + "") + licenseRelative; + + } else { + return repo.replace("git://", "https://").replace("github.com", "raw.githubusercontent.com") + "/master" + + licenseRelative; + + } } } return repo; diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java index 39d61ec1..d4a5fac4 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java @@ -139,8 +139,8 @@ public void testLicenseUrlIfLicenseFileFoundAndGithub() { List lapc = this.application.getApplicationComponents(); boolean found = false; for (ApplicationComponent ap : lapc) { - if (ap.getArtifactId().equals("foo") - && ap.getRawLicenses().get(0).getLicenseUrl().equals("https://github.com/somebody/foo/raw/master/LICENSE")) { + if (ap.getArtifactId().equals("foo") && ap.getRawLicenses().get(0).getLicenseUrl() + .equals("https://raw.githubusercontent.com/somebody/foo/master/LICENSE")) { found = true; break; } diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index b9a5eadc..77c0964a 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1614,6 +1614,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.16.0:: +* https://github.com/devonfw/solicitor/pull/212: Improvement in determining License-URL within NpmLicenseCheckerReader. Changes in 1.15.0:: * https://github.com/devonfw/solicitor/issues/208: Add two new lifecycle methods to `SolicitorLifecycleListener`. From a89af3841b056cb4e7d3ea476ee4e27c4fd9930a Mon Sep 17 00:00:00 2001 From: sauleh7 <105782142+sauleh7@users.noreply.github.com> Date: Thu, 16 Nov 2023 18:01:04 +0100 Subject: [PATCH 080/139] File name strategy (#217) * Edited the file long name strategy adjusted * Modified the filename naming strategy for cache files * Wrote Unit Test and also a bit changed java file * Add Parameterized Tests * Refactored CachingContentProviderBase * Recfactored, setted Logik * minor test impoprvements, added double underscores between parts of original url and the hash * adopted user guide --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../content/CachingContentProviderBase.java | 48 ++++++++++++++--- .../CachingContentProviderBaseTest.java | 53 +++++++++++++++++++ documentation/master-solicitor.asciidoc | 10 +++- 3 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/common/content/CachingContentProviderBaseTest.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/content/CachingContentProviderBase.java b/core/src/main/java/com/devonfw/tools/solicitor/common/content/CachingContentProviderBase.java index 0ec82c1f..eeee65a8 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/content/CachingContentProviderBase.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/content/CachingContentProviderBase.java @@ -6,6 +6,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Collection; import java.util.Scanner; @@ -13,6 +15,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; import com.devonfw.tools.solicitor.common.UrlInputStreamFactory; /** @@ -30,6 +33,8 @@ public abstract class CachingContentProviderBase extends Abst private ContentProvider nextContentProvider; + static final int MAX_KEY_LENGTH = 250; + /** * The Constructor. * @@ -40,7 +45,6 @@ public CachingContentProviderBase(ContentFactory contentFactory, ContentProvi super(contentFactory); this.nextContentProvider = nextContentProvider; - } /** @@ -52,21 +56,51 @@ public CachingContentProviderBase(ContentFactory contentFactory, ContentProvi protected abstract Collection getCacheUrls(String key); /** - * Calculate the key for the given web content URL. + * Calculate the cache key for the given web content URL. * * @param url the URL of the web content * @return the cache key */ public String getKey(String url) { - /** - * Normalize URL to http - */ if (url.startsWith("https")) { url = url.replace("https", "http"); } String result = url.replaceAll("\\W", "_"); - return result; + // Check if the filename length exceeds the maximum length + if (result.length() <= MAX_KEY_LENGTH) { + return result; // If it's within the limit, use it as is. + } else { + // If the filename length is too long, create a modified filename. + String prefix = result.substring(0, 40); + String suffix = result.substring(result.length() - 40); + + // Calculate a hash value of the original filename (e.g., using SHA-256) + String hash = generateHash(result); + return prefix + "__" + hash + "__" + suffix; + } + } + + /** + * Generates a SHA-256 hash of the input string. + * + * @param input The input string to be hashed. + * @return A hexadecimal string representation of the SHA-256 hash. + * + */ + private String generateHash(String input) { + + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(input.getBytes()); + StringBuilder hexString = new StringBuilder(); + for (byte b : hash) { + hexString.append(String.format("%02x", b)); + } + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + throw new SolicitorRuntimeException("SHA-256 hashing algorithm not available.", e); + } } /** @@ -111,4 +145,4 @@ protected C loadFromNext(String url) { return this.nextContentProvider.getContentForUri(url); } -} +} \ No newline at end of file diff --git a/core/src/test/java/com/devonfw/tools/solicitor/common/content/CachingContentProviderBaseTest.java b/core/src/test/java/com/devonfw/tools/solicitor/common/content/CachingContentProviderBaseTest.java new file mode 100644 index 00000000..1744133c --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/common/content/CachingContentProviderBaseTest.java @@ -0,0 +1,53 @@ +package com.devonfw.tools.solicitor.common.content; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collection; + +import org.junit.jupiter.api.Test; + +/** + * Unit test for the {@link CachingContentProviderBase#getKey(String)} method. + */ +class CachingContentProviderBaseTest { + + // Dummy implementation of CachingContentProviderBase for testing + static class TestCachingContentProvider extends CachingContentProviderBase { + public TestCachingContentProvider() { + + super(null, null); // ContentFactory and ContentProvider not needed for this test + } + + @Override + protected Collection getCacheUrls(String key) { + + return null; + } + } + + @Test + void shouldGenerateCorrectKeyForUrlOfLength250() { + + TestCachingContentProvider cachingContentProvider = new TestCachingContentProvider(); + + // Create a URL of length 250 (classical logic should be used) + String longUrl250 = "http://example.com/clear/and/concise/url/for/testing/purposes/with/exactly/250/characters/in/total/including/letters/numbers/special/characters/as/appropriate/for/clarity/this/is/a/very/long/url/the/maximum/filename/length/just/to/reach/to/length/250"; + String longResult250 = cachingContentProvider.getKey(longUrl250); + assertTrue(longResult250.length() == 250, // length should be unchanged + "Modified filename length exceeds the maximum for URL of length 250"); + } + + @Test + void shouldGenerateCorrectKeyForUrlOfLength251() { + + TestCachingContentProvider cachingContentProvider = new TestCachingContentProvider(); + + // Create a URL of length 251 (new approach should be used) + String longUrl251 = "http://example.com/clear/and/concise/url/for/testing/purposes/with/exactly/251/characters/in/total/including/letters/numbers/special/characters/as/appropriate/for/clarity/this/is/a/very/long/url/the/maximum/filenames/length/just/to/reach/to/length/251"; + String longResult251 = cachingContentProvider.getKey(longUrl251); + assertEquals(40 + 2 + 64 + 2 + 40, longResult251.length(), + "Modified filename length is incorrect for URL of length 251"); + } + +} diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 77c0964a..53d45d87 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1261,7 +1261,14 @@ The determined content is available as `NormalizedLicense.effectiveNormalizedLic === Encoding of URLs When creating the resource or filename for given URLs in the above steps the following encoding scheme will be applied to ensure that always a valid name can be created: -All "non-word" characters (i.e. characters outside the set `[a-zA-Z_0-9]`) are replaced by underscores ("`_`"). +* If the scheme is `https` it will be replaced with `http`. +* All "non-word" characters (i.e. characters outside the set `[a-zA-Z_0-9]`) are replaced by underscores ("`_`"). +* In case that the resulting filename exceeds a length of 250 it will be replaced by a new name concatenated from +** the first 40 characters of the (too) long filename +** two underscores +** a sha256 (hex encoded) of the (too) long filename +** two underscores +** the last 40 characters of the (too) long filename == Guessing of license URLs Fetching the license content `NormalizedLicense.effectiveNormalizedLicenseContent` based on the URL in `NormalizedLicense.effectiveNormalizedLicenseUrl` will often result in content which is in HTML format instead of plain text and is not properly rendered when included in reports. @@ -1615,6 +1622,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.16.0:: * https://github.com/devonfw/solicitor/pull/212: Improvement in determining License-URL within NpmLicenseCheckerReader. +* https://github.com/devonfw/solicitor/issues/213: Avoid (too) long filenames when caching license texts oder licenseurls. See <>. Changes in 1.15.0:: * https://github.com/devonfw/solicitor/issues/208: Add two new lifecycle methods to `SolicitorLifecycleListener`. From 6b18d0b76f7d4af77019dbe1f6915cd7fd96f78c Mon Sep 17 00:00:00 2001 From: duph97 Date: Fri, 17 Nov 2023 18:03:04 +0100 Subject: [PATCH 081/139] Update dependencies and plugins (#219) * Update dependencies * Add release note * replaced no longer supported pdf-style /pdf-stylesdir with pdf-theme / pdf-themesdir --------- Co-authored-by: Oliver Hecker <8004361+ohecker@users.noreply.github.com> --- app/pom.xml | 2 +- core/pom.xml | 10 +++++----- documentation/master-solicitor.asciidoc | 1 + documentation/pom.xml | 14 +++++++------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index d3a9058b..1ca9ee42 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.6.3 + 2.7.17 diff --git a/core/pom.xml b/core/pom.xml index b7ed8275..20a74d94 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -98,12 +98,12 @@ org.apache.commons commons-csv - 1.9.0 + 1.10.0 commons-codec commons-codec - 1.15 + 1.16.0 org.apache.velocity @@ -130,7 +130,7 @@ commons-cli commons-cli - 1.5.0 + 1.6.0 org.apache.maven @@ -161,7 +161,7 @@ com.auth0 java-jwt - 4.2.1 + 4.4.0 ${project.groupId} @@ -231,7 +231,7 @@ org.codehaus.mojo license-maven-plugin - 2.0.0 + 2.3.0 validate diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 53d45d87..c283aa25 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1623,6 +1623,7 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.16.0:: * https://github.com/devonfw/solicitor/pull/212: Improvement in determining License-URL within NpmLicenseCheckerReader. * https://github.com/devonfw/solicitor/issues/213: Avoid (too) long filenames when caching license texts oder licenseurls. See <>. +* https://github.com/devonfw/solicitor/issues/218: Update dependencies to latest version. Changes in 1.15.0:: * https://github.com/devonfw/solicitor/issues/208: Add two new lifecycle methods to `SolicitorLifecycleListener`. diff --git a/documentation/pom.xml b/documentation/pom.xml index f3a9c34b..40bd5cc7 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -25,9 +25,9 @@ - 2.2.2 - 1.6.2 - 2.5.3 + 2.2.4 + 2.3.9 + 2.5.10 9.3.3.0 dd-MM-yy_HH:mm ${maven.build.timestamp} @@ -56,7 +56,7 @@ asciidoctor-maven-plugin - [2.2.2,) + [2.2.4,) @@ -154,8 +154,8 @@ coderay book - ${project.basedir}/theme - solicitor + ${project.basedir}/theme + solicitor font @@ -169,7 +169,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.3.0 + 3.4.0 attach-doc From 0c17624a76b9f35b3fd6ccd7959b9baef61e342d Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:00:39 +0100 Subject: [PATCH 082/139] Version 1.16.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 1ca9ee42..785dd2fc 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.16.0-SNAPSHOT + 1.16.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 20a74d94..8d3e6b71 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.16.0-SNAPSHOT + 1.16.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 40bd5cc7..3430fcf0 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.16.0-SNAPSHOT + 1.16.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 2b2ce88a..d89e7d8b 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.16.0-SNAPSHOT + 1.16.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index bd4d94ab..dbe5c8a5 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.16.0-SNAPSHOT + 1.16.0 pom Solicitor Aggregator From 0c41ab4655428773e92bbbdc36996596d4e94d0e Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 17 Nov 2023 22:07:04 +0100 Subject: [PATCH 083/139] Set version to SNAPSHOT of next minor release --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 785dd2fc..3cb23395 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.16.0 + 1.17.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 8d3e6b71..a82163b2 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.16.0 + 1.17.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index c283aa25..a3aedceb 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1620,6 +1620,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.17.0:: + Changes in 1.16.0:: * https://github.com/devonfw/solicitor/pull/212: Improvement in determining License-URL within NpmLicenseCheckerReader. * https://github.com/devonfw/solicitor/issues/213: Avoid (too) long filenames when caching license texts oder licenseurls. See <>. diff --git a/documentation/pom.xml b/documentation/pom.xml index 3430fcf0..c748ba46 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.16.0 + 1.17.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index d89e7d8b..ec01bc2c 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.16.0 + 1.17.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index dbe5c8a5..ef089c0e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.16.0 + 1.17.0-SNAPSHOT pom Solicitor Aggregator From b7399dcef2816e736841bfe6c33b1c8ce84c4f16 Mon Sep 17 00:00:00 2001 From: duph97 Date: Mon, 20 Nov 2023 11:18:40 +0100 Subject: [PATCH 084/139] Fix spell check error (#220) * Add https and sha to dict * Fix typo --- documentation/master-solicitor.asciidoc | 2 +- solicitor.dict | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index a3aedceb..bf70422b 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1624,7 +1624,7 @@ Changes in 1.17.0:: Changes in 1.16.0:: * https://github.com/devonfw/solicitor/pull/212: Improvement in determining License-URL within NpmLicenseCheckerReader. -* https://github.com/devonfw/solicitor/issues/213: Avoid (too) long filenames when caching license texts oder licenseurls. See <>. +* https://github.com/devonfw/solicitor/issues/213: Avoid (too) long filenames when caching license texts or licenseurls. See <>. * https://github.com/devonfw/solicitor/issues/218: Update dependencies to latest version. Changes in 1.15.0:: diff --git a/solicitor.dict b/solicitor.dict index a4b1fc7f..3c785fb2 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -15,6 +15,7 @@ eug fas gradle http +https jwt licenseRefUrl licenseToIgnoreN @@ -27,6 +28,7 @@ oss RHS SBOMs scancode +sha SOLI sql Unlicense From 562ec471225b7e36df3ba1d9bc096d93a44a36da Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 22 Dec 2023 16:43:17 +0100 Subject: [PATCH 085/139] Make use of data status (#222) * Introduce new ComponentInfoData object within ComponentInfo * removed ComponentInfo.isDataAvailable() * ComponentInfoProvider.getComponentInfo() returns object even if case that no component info was found * Full dataStatus * handle Exception thrown for download/scan errors locally * use constants for setting dataStatus values * adopted documentation --------- Co-authored-by: Mahmoud Alkam --- .../componentinfo/ComponentInfo.java | 72 +-- .../componentinfo/ComponentInfoData.java | 68 +++ .../ComponentInfoInventoryProcessor.java | 70 ++- .../componentinfo/ComponentInfoProvider.java | 5 +- .../componentinfo/DataStatusValue.java | 45 ++ .../DefaultComponentInfoDataImpl.java | 194 ++++++++ .../DefaultComponentInfoImpl.java | 182 ++------ .../curation/ComponentInfoCuratorImpl.java | 12 +- .../CuratingComponentInfoAdapter.java | 53 ++- .../FileScancodeRawComponentInfoProvider.java | 30 +- ...FilteredScancodeComponentInfoProvider.java | 45 +- .../scancode/ScancodeComponentInfo.java | 418 ++++++++++-------- .../ScancodeProcessingFailedException.java | 37 ++ .../ScancodeRawComponentInfoProvider.java | 5 +- ...redScancodeComponentInfoProviderTests.java | 41 +- .../ScancodeComponentInfoAdapterTest.java | 46 +- documentation/master-solicitor.asciidoc | 18 +- 17 files changed, 839 insertions(+), 502 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoData.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DataStatusValue.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoDataImpl.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeProcessingFailedException.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java index 6104fbc4..3b54ad37 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfo.java @@ -1,11 +1,10 @@ package com.devonfw.tools.solicitor.componentinfo; -import java.util.Collection; import java.util.List; /** - * Data structure which holds information about a component which comes from an external data source, like the results - * of a scancode scan. + * Data structure which encapsulates the information about a component which comes from an external data source, like + * the results of a scancode scan. * */ public interface ComponentInfo { @@ -18,63 +17,8 @@ public interface ComponentInfo { String getPackageUrl(); /** - * Gets all copyrights. - * - * @return the copyrights - */ - Collection getCopyrights(); - - /** - * Gets all licenses. - * - * @return all licenses - */ - Collection getLicenses(); - - /** - * Gets the url to the notice file (if any) - * - * @return url of the notice file - */ - String getNoticeFileUrl(); - - /** - * Gets the contents of the notice file (if any) - * - * @return contents of the notice file - */ - String getNoticeFileContent(); - - /** - * Gets the url of the projects homepage, - * - * @return url to the projects homepage - */ - String getHomepageUrl(); - - /** - * Gets the url of the source code repository. - * - * @return url to source code repository - */ - String getSourceRepoUrl(); - - /** - * Gets the url for downloading the package/component. - * - * @return url to download the package - */ - String getPackageDownloadUrl(); - - /** - * Gets the url for downloading the sources of the package/component. - * - * @return url to download the sources of the package/component - */ - String getSourceDownloadUrl(); - - /** - * This method gets the field dataStatus. + * This method gets the field dataStatus. If no data is available this indicates why there is no data + * available. * * @return the field dataStatus */ @@ -87,4 +31,12 @@ public interface ComponentInfo { */ List getTraceabilityNotes(); + /** + * Gets the data on the component. + * + * @return the detailed info on the component. In case that no data is available (e.g. because there is no scancode + * result available) this method will return null. + */ + ComponentInfoData getComponentInfoData(); + } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoData.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoData.java new file mode 100644 index 00000000..783d8ba8 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoData.java @@ -0,0 +1,68 @@ +package com.devonfw.tools.solicitor.componentinfo; + +import java.util.Collection; + +/** + * Data structure which holds information about a component which comes from an external data source, like the results + * of a scancode scan. + * + */ +public interface ComponentInfoData { + + /** + * Gets all copyrights. + * + * @return the copyrights + */ + Collection getCopyrights(); + + /** + * Gets all licenses. + * + * @return all licenses + */ + Collection getLicenses(); + + /** + * Gets the url to the notice file (if any) + * + * @return url of the notice file + */ + String getNoticeFileUrl(); + + /** + * Gets the contents of the notice file (if any) + * + * @return contents of the notice file + */ + String getNoticeFileContent(); + + /** + * Gets the url of the projects homepage, + * + * @return url to the projects homepage + */ + String getHomepageUrl(); + + /** + * Gets the url of the source code repository. + * + * @return url to source code repository + */ + String getSourceRepoUrl(); + + /** + * Gets the url for downloading the package/component. + * + * @return url to download the package + */ + String getPackageDownloadUrl(); + + /** + * Gets the url for downloading the sources of the package/component. + * + * @return url to download the sources of the package/component + */ + String getSourceDownloadUrl(); + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 04fb064c..b631ab4f 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -40,6 +40,16 @@ public void add(Statistics statistics) { } } + /** + * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that data is available. + */ + private static final String DA_STATUS_PREFIX = "DA:"; + + /** + * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that no data is available. + */ + private static final String ND_STATUS_PREFIX = "ND:"; + /** * Origin data for raw license objects created by this Class. Due to compatibility reasons this is named "scancode" * even due to the fact that it might originate from other sources. @@ -101,6 +111,7 @@ public void processInventory(ModelRoot modelRoot) { * @return A {@link Statistics} object representing the processing statistics. * @throws SolicitorRuntimeException If there is an exception when reading the component info data source. */ + // TODO: ohecker: refactor this method private Statistics processApplicationComponent(ApplicationComponent ac) { Statistics statistics = new Statistics(); @@ -108,40 +119,48 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { if (ac.getPackageUrl() != null) { // Try to get component information from the available ComponentInfoAdapters + ComponentInfo componentInfoCandidate = null; ComponentInfo componentInfo = null; + ComponentInfoData componentInfoData = null; try { for (ComponentInfoProvider cia : this.componentInfoAdapters) { - componentInfo = cia.getComponentInfo(ac.getPackageUrl(), this.curationDataSelector); - // stop querying further adapters if some info was returned - if (componentInfo != null) { - break; + componentInfoCandidate = cia.getComponentInfo(ac.getPackageUrl(), this.curationDataSelector); + if (componentInfoCandidate != null) { + componentInfo = componentInfoCandidate; + // stop querying further adapters if some info was returned + componentInfoData = componentInfo.getComponentInfoData(); + if (componentInfoData != null) { + break; + } } } } catch (ComponentInfoAdapterException e) { throw new SolicitorRuntimeException("Exception when reading component info data source", e); } - if (componentInfo != null) { + if (componentInfo == null) { + // all adapters disabled + ac.setDataStatus(ND_STATUS_PREFIX + DataStatusValue.DISABLED); + } else if (componentInfoData != null) { statistics.componentsWithComponentInfo = 1; - // Set dataStatus and traceabilityNotes of the ApplicationComponent - ac.setDataStatus(componentInfo.getDataStatus()); + ac.setDataStatus(DA_STATUS_PREFIX + componentInfo.getDataStatus()); // Format and set the traceabilityNotes in the ApplicationComponent String formattedTraceabilityNotes = formatTraceabilityNotes(componentInfo); ac.setTraceabilityNotes(formattedTraceabilityNotes); // Update the notice file URL and content if available - if (componentInfo.getNoticeFileUrl() != null) { - ac.setNoticeFileUrl(componentInfo.getNoticeFileUrl()); + if (componentInfoData.getNoticeFileUrl() != null) { + ac.setNoticeFileUrl(componentInfoData.getNoticeFileUrl()); } - if (componentInfo.getNoticeFileContent() != null) { - ac.setNoticeFileContent(componentInfo.getNoticeFileContent()); + if (componentInfoData.getNoticeFileContent() != null) { + ac.setNoticeFileContent(componentInfoData.getNoticeFileContent()); } // Process licenses if available - if (componentInfo.getLicenses().size() > 0) { + if (componentInfoData.getLicenses().size() > 0) { ac.removeAllRawLicenses(); - for (LicenseInfo li : componentInfo.getLicenses()) { + for (LicenseInfo li : componentInfoData.getLicenses()) { addRawLicense(ac, li.getSpdxid(), li.getLicenseUrl(), li.getGivenLicenseText(), ORIGIN_COMPONENTINFO); } } else { @@ -154,26 +173,31 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { } } - String copyrights = String.join("\n", componentInfo.getCopyrights()); + String copyrights = String.join("\n", componentInfoData.getCopyrights()); ac.setCopyrights(copyrights); // check whether VendorUrl is included in input file or not - if (componentInfo.getHomepageUrl() != null) { - ac.setOssHomepage(componentInfo.getHomepageUrl()); + if (componentInfoData.getHomepageUrl() != null) { + ac.setOssHomepage(componentInfoData.getHomepageUrl()); } // check whether Source ReopUrl is included in input file or not - if (componentInfo.getSourceRepoUrl() != null) { - ac.setSourceRepoUrl(componentInfo.getSourceRepoUrl()); + if (componentInfoData.getSourceRepoUrl() != null) { + ac.setSourceRepoUrl(componentInfoData.getSourceRepoUrl()); } // always overwrite the download URLs - even if componentInfo does not contain any data - ac.setPackageDownloadUrl(componentInfo.getPackageDownloadUrl()); - ac.setSourceDownloadUrl(componentInfo.getSourceDownloadUrl()); - + ac.setPackageDownloadUrl(componentInfoData.getPackageDownloadUrl()); + ac.setSourceDownloadUrl(componentInfoData.getSourceDownloadUrl()); } else { - // no ComponentInfos info found for ac + // no adapter delivered data, set the status of the last queried adapter + ac.setDataStatus(ND_STATUS_PREFIX + componentInfo.getDataStatus()); + String formattedTraceabilityNotes = formatTraceabilityNotes(componentInfo); + ac.setTraceabilityNotes(formattedTraceabilityNotes); + } + } else { - // can this happen? + // ac did not contain PackageUrl + // TODO: can this happen? } return statistics; } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java index 22df6792..125eb395 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java @@ -14,7 +14,10 @@ public interface ComponentInfoProvider { * @param packageUrl The identifier of the package for which information is requested * @param curationDataSelector identifies which source should be used for the curation data. null * indicates that the default should be used. - * @return the data for the component. null is returned if no data is available, + * @return the data for the component. If the bean implementing this interface is deactivated (e.g. via some feature + * flag configuration) this method will return null. Otherwise a non-null object + * will be refurned. If there is no actual data available for the requested component calling + * {@link ComponentInfo#getComponentInfoData()} on the returned object will return null. * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be returned in such a case. */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DataStatusValue.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DataStatusValue.java new file mode 100644 index 00000000..b833da37 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DataStatusValue.java @@ -0,0 +1,45 @@ +package com.devonfw.tools.solicitor.componentinfo; + +/** + * Constants for data status values within Solicitor. + */ +public class DataStatusValue { + + /** + * No data available. Functionality is disabled. + */ + public static final String DISABLED = "DISABLED"; + + /** + * No data available. No scan results existing and no indication that attempting that download/scanning has failed. + */ + public static final String NOT_AVAILABLE = "NOT_AVAILABLE"; + + /** + * No data available. No scan results existing. Processing (downloading or scanning) had failed. + */ + public static final String PROCESSING_FAILED = "PROCESSING_FAILED"; + + /** + * Data available. Issues were detected in the data which probably need to be curated. + */ + public static final String WITH_ISSUES = "WITH_ISSUES"; + + /** + * Data available. No curations applied. No issues were detected. + */ + public static final String NO_ISSUES = "NO_ISSUES"; + + /** + * Data available. Curations were applied. No issues were detected. + */ + public static final String CURATED = "CURATED"; + + /** + * Private constructor to prevent instantiation. + */ + private DataStatusValue() { + + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoDataImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoDataImpl.java new file mode 100644 index 00000000..ede874f2 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoDataImpl.java @@ -0,0 +1,194 @@ +package com.devonfw.tools.solicitor.componentinfo; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +/** + * Default POJO implementation of a {@link ComponentInfoData}. + * + */ +public class DefaultComponentInfoDataImpl implements ComponentInfoData { + + private Collection copyrights; + + private Collection licenses; + + private String noticeFileUrl; + + private String noticeFileContent; + + private String homepageUrl; + + private String sourceRepoUrl; + + private String packageDownloadUrl; + + private String sourceDownloadUrl; + + /** + * The constructor. + */ + public DefaultComponentInfoDataImpl() { + + this.copyrights = new ArrayList<>(); + this.licenses = new ArrayList<>(); + } + + /** + * Copy-Constructor. Allows to construct an instance of the class from another {@link ComponentInfoData} instance. + * Members are deep copied. Any changes to the new instance do not affect the original source. + * + * @param source the instance to copy the data from + * + */ + public DefaultComponentInfoDataImpl(ComponentInfoData source) { + + this(); + this.homepageUrl = source.getHomepageUrl(); + this.noticeFileContent = source.getNoticeFileContent(); + this.noticeFileUrl = source.getNoticeFileUrl(); + this.packageDownloadUrl = source.getPackageDownloadUrl(); + this.sourceDownloadUrl = source.getSourceDownloadUrl(); + this.sourceRepoUrl = source.getSourceRepoUrl(); + for (String copyright : source.getCopyrights()) { + addCopyright(copyright); + } + for (LicenseInfo licenseInfo : source.getLicenses()) { + addLicense(licenseInfo); + } + } + + /** + * @param noticeFileUrl new value of {@link #getNoticeFileUrl}. + */ + public void setNoticeFileUrl(String noticeFileUrl) { + + this.noticeFileUrl = noticeFileUrl; + } + + /** + * @param noticeFileContent new value of {@link #getNoticeFileContent}. + */ + public void setNoticeFileContent(String noticeFileContent) { + + this.noticeFileContent = noticeFileContent; + } + + /** + * @param homepageUrl new value of {@link #getHomepageUrl}. + */ + public void setHomepageUrl(String homepageUrl) { + + this.homepageUrl = homepageUrl; + } + + /** + * @param sourceRepoUrl new value of {@link #getSourceRepoUrl}. + */ + public void setSourceRepoUrl(String sourceRepoUrl) { + + this.sourceRepoUrl = sourceRepoUrl; + } + + /** + * @param packageDownloadUrl new value of {@link #getPackageDownloadUrl}. + */ + public void setPackageDownloadUrl(String packageDownloadUrl) { + + this.packageDownloadUrl = packageDownloadUrl; + } + + /** + * @param sourceDownloadUrl new value of {@link #getSourceDownloadUrl}. + */ + public void setSourceDownloadUrl(String sourceDownloadUrl) { + + this.sourceDownloadUrl = sourceDownloadUrl; + } + + @Override + public Collection getCopyrights() { + + return Collections.unmodifiableCollection(this.copyrights); + } + + /** + * Clears the collection of copyrights. + */ + public void clearCopyrights() { + + this.copyrights = new ArrayList<>(); + } + + /** + * Adds a copyright string to the list of already existing copyrights. + * + * @param copyright the copyrigt sring to add + */ + public void addCopyright(String copyright) { + + this.copyrights.add(copyright); + } + + @Override + public Collection getLicenses() { + + return Collections.unmodifiableCollection(this.licenses); + } + + /** + * Clears the collection of licenses. + */ + public void clearLicenses() { + + this.licenses = new ArrayList<>(); + } + + /** + * Adds a {@link LicenseInfo} instance to the collection of licenses. + * + * @param licenseInfo the license to add + */ + public void addLicense(LicenseInfo licenseInfo) { + + this.licenses.add(new DefaultLicenseInfoImpl(licenseInfo)); + } + + @Override + public String getNoticeFileUrl() { + + return this.noticeFileUrl; + } + + @Override + public String getNoticeFileContent() { + + return this.noticeFileContent; + } + + @Override + public String getHomepageUrl() { + + return this.homepageUrl; + } + + @Override + public String getSourceRepoUrl() { + + return this.sourceRepoUrl; + } + + @Override + public String getPackageDownloadUrl() { + + return this.packageDownloadUrl; + } + + @Override + public String getSourceDownloadUrl() { + + return this.sourceDownloadUrl; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoImpl.java index 162dbf52..4cabf541 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/DefaultComponentInfoImpl.java @@ -1,7 +1,6 @@ package com.devonfw.tools.solicitor.componentinfo; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; @@ -13,36 +12,33 @@ public class DefaultComponentInfoImpl implements ComponentInfo { private String packageUrl; - private Collection copyrights; - - private Collection licenses; - - private String noticeFileUrl; - - private String noticeFileContent; - - private String homepageUrl; - - private String sourceRepoUrl; - - private String packageDownloadUrl; - - private String sourceDownloadUrl; - private String dataStatus; private List traceabilityNotes; + private DefaultComponentInfoDataImpl componentInfoData; + /** * The constructor. */ public DefaultComponentInfoImpl() { - this.copyrights = new ArrayList<>(); - this.licenses = new ArrayList<>(); this.traceabilityNotes = new ArrayList<>(); } + /** + * The constructor. + * + * @param packageUrl the PackageUrl of the component + * @param dataStatus the dataStatus + */ + public DefaultComponentInfoImpl(String packageUrl, String dataStatus) { + + this(); + this.packageUrl = packageUrl; + this.dataStatus = dataStatus; + } + /** * Copy-Constructor. Allows to construct an instance of the class from another {@link ComponentInfo} instance. Members * are deep copied. Any changes to the new instance do not affect the original source. @@ -55,20 +51,11 @@ public DefaultComponentInfoImpl(ComponentInfo source) { this(); this.packageUrl = source.getPackageUrl(); this.dataStatus = source.getDataStatus(); - this.homepageUrl = source.getHomepageUrl(); - this.noticeFileContent = source.getNoticeFileContent(); - this.noticeFileUrl = source.getNoticeFileUrl(); - this.packageDownloadUrl = source.getPackageDownloadUrl(); - this.sourceDownloadUrl = source.getSourceDownloadUrl(); - this.sourceRepoUrl = source.getSourceRepoUrl(); - for (String copyright : source.getCopyrights()) { - addCopyright(copyright); - } for (String traceabilityNote : source.getTraceabilityNotes()) { addTraceabillityNote(traceabilityNote); } - for (LicenseInfo licenseInfo : source.getLicenses()) { - addLicense(licenseInfo); + if (source.getComponentInfoData() != null) { + this.componentInfoData = new DefaultComponentInfoDataImpl(source.getComponentInfoData()); } } @@ -80,54 +67,6 @@ public void setPackageUrl(String packageUrl) { this.packageUrl = packageUrl; } - /** - * @param noticeFileUrl new value of {@link #getNoticeFileUrl}. - */ - public void setNoticeFileUrl(String noticeFileUrl) { - - this.noticeFileUrl = noticeFileUrl; - } - - /** - * @param noticeFileContent new value of {@link #getNoticeFileContent}. - */ - public void setNoticeFileContent(String noticeFileContent) { - - this.noticeFileContent = noticeFileContent; - } - - /** - * @param homepageUrl new value of {@link #getHomepageUrl}. - */ - public void setHomepageUrl(String homepageUrl) { - - this.homepageUrl = homepageUrl; - } - - /** - * @param sourceRepoUrl new value of {@link #getSourceRepoUrl}. - */ - public void setSourceRepoUrl(String sourceRepoUrl) { - - this.sourceRepoUrl = sourceRepoUrl; - } - - /** - * @param packageDownloadUrl new value of {@link #getPackageDownloadUrl}. - */ - public void setPackageDownloadUrl(String packageDownloadUrl) { - - this.packageDownloadUrl = packageDownloadUrl; - } - - /** - * @param sourceDownloadUrl new value of {@link #getSourceDownloadUrl}. - */ - public void setSourceDownloadUrl(String sourceDownloadUrl) { - - this.sourceDownloadUrl = sourceDownloadUrl; - } - /** * @param dataStatus new value of {@link #getDataStatus}. */ @@ -136,52 +75,12 @@ public void setDataStatus(String dataStatus) { this.dataStatus = dataStatus; } - @Override - public Collection getCopyrights() { - - return Collections.unmodifiableCollection(this.copyrights); - } - /** - * Clears the collection of copyrights. + * @param componentInfoData new value of {@link #getComponentInfoData}. */ - public void clearCopyrights() { + public void setComponentInfoData(DefaultComponentInfoDataImpl componentInfoData) { - this.copyrights = new ArrayList<>(); - } - - /** - * Adds a copyright string to the list of already existing copyrights. - * - * @param copyright the copyrigt sring to add - */ - public void addCopyright(String copyright) { - - this.copyrights.add(copyright); - } - - @Override - public Collection getLicenses() { - - return Collections.unmodifiableCollection(this.licenses); - } - - /** - * Clears the collection of licenses. - */ - public void clearLicenses() { - - this.licenses = new ArrayList<>(); - } - - /** - * Adds a {@link LicenseInfo} instance to the collection of licenses. - * - * @param licenseInfo the license to add - */ - public void addLicense(LicenseInfo licenseInfo) { - - this.licenses.add(new DefaultLicenseInfoImpl(licenseInfo)); + this.componentInfoData = componentInfoData; } @Override @@ -191,45 +90,18 @@ public String getPackageUrl() { } @Override - public String getNoticeFileUrl() { - - return this.noticeFileUrl; - } - - @Override - public String getNoticeFileContent() { - - return this.noticeFileContent; - } - - @Override - public String getHomepageUrl() { - - return this.homepageUrl; - } - - @Override - public String getSourceRepoUrl() { - - return this.sourceRepoUrl; - } - - @Override - public String getPackageDownloadUrl() { - - return this.packageDownloadUrl; - } - - @Override - public String getSourceDownloadUrl() { + public String getDataStatus() { - return this.sourceDownloadUrl; + return this.dataStatus; } + /** + * @return componentInfoData + */ @Override - public String getDataStatus() { + public DefaultComponentInfoDataImpl getComponentInfoData() { - return this.dataStatus; + return this.componentInfoData; } @Override diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java index a0b2e74d..ada9a5aa 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java @@ -8,6 +8,7 @@ import com.devonfw.tools.solicitor.componentinfo.ComponentContentProvider; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.DataStatusValue; import com.devonfw.tools.solicitor.componentinfo.DefaultComponentInfoImpl; import com.devonfw.tools.solicitor.componentinfo.DefaultLicenseInfoImpl; import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; @@ -61,6 +62,7 @@ public ComponentInfo curate(ComponentInfo componentInfo, String curationDataSele if (foundCuration != null) { DefaultComponentInfoImpl componentInfoImpl = new DefaultComponentInfoImpl(componentInfo); applyFoundCurations(componentInfoImpl, foundCuration); + componentInfoImpl.setDataStatus(DataStatusValue.CURATED); return componentInfoImpl; } else { return componentInfo; @@ -70,16 +72,16 @@ public ComponentInfo curate(ComponentInfo componentInfo, String curationDataSele private void applyFoundCurations(DefaultComponentInfoImpl componentInfo, ComponentInfoCuration curation) { if (curation.getCopyrights() != null) { - componentInfo.clearCopyrights(); + componentInfo.getComponentInfoData().clearCopyrights(); for (String copyright : curation.getCopyrights()) { - componentInfo.addCopyright(copyright); + componentInfo.getComponentInfoData().addCopyright(copyright); } } if (curation.getUrl() != null) { - componentInfo.setSourceRepoUrl(curation.getUrl()); + componentInfo.getComponentInfoData().setSourceRepoUrl(curation.getUrl()); } if (curation.getLicenses() != null) { - componentInfo.clearLicenses(); + componentInfo.getComponentInfoData().clearLicenses(); for (LicenseInfoCuration licenseCuration : curation.getLicenses()) { String license = licenseCuration.getLicense(); String url = licenseCuration.getUrl(); @@ -91,7 +93,7 @@ private void applyFoundCurations(DefaultComponentInfoImpl componentInfo, Compone licenseInfo.setSpdxId(license); licenseInfo.setLicenseUrl(url); licenseInfo.setGivenLicenseText(givenLicenseText); - componentInfo.addLicense(licenseInfo); + componentInfo.getComponentInfoData().addLicense(licenseInfo); } } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java index ceafa745..b71be000 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java @@ -1,8 +1,15 @@ package com.devonfw.tools.solicitor.componentinfo.curation; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.DataStatusValue; +import com.devonfw.tools.solicitor.componentinfo.DefaultComponentInfoImpl; +import com.devonfw.tools.solicitor.componentinfo.LicenseInfo; /** * A {@link ComponentInfoAdapter} which takes filtered {@link ComponentInfo} data from the configuret @@ -35,8 +42,7 @@ public CuratingComponentInfoAdapter(FilteredComponentInfoProvider filteredCompon * @param packageUrl The identifier of the package for which information is requested * @param curationDataSelector identifies which source should be used for the curation data. null * indicates that the default should be used. - * @return the data derived from the scancode results after applying any defined curation. null is - * returned if no data is available, + * @return the data derived from the scancode results after applying any defined curation. * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be return in such a case. */ @@ -48,11 +54,13 @@ public ComponentInfo getComponentInfo(String packageUrl, String curationDataSele ComponentInfo componentInfo = this.filteredComponentInfoProvider.getComponentInfo(packageUrl, curationDataSelector); - if (componentInfo == null) { - return null; + if (componentInfo == null || componentInfo.getComponentInfoData() == null) { + return componentInfo; } componentInfo = this.componentInfoCurator.curate(componentInfo, curationDataSelector); + componentInfo = checkForIssues(componentInfo); + return componentInfo; } else { @@ -61,6 +69,43 @@ public ComponentInfo getComponentInfo(String packageUrl, String curationDataSele } + /** + * Checks for issues in the given {@link ComponentInfo}. Issues include licenses falling into a defined set of keys. + * + * @param componentInfo The component information to check for issues. + * @return the component info with the status set to "WITH_ISSUES" if issues are found. + */ + private ComponentInfo checkForIssues(ComponentInfo componentInfo) { + + if (componentInfo.getComponentInfoData() == null) { + return componentInfo; + } + + Collection licenses = componentInfo.getComponentInfoData().getLicenses(); + if (licenses == null || licenses.isEmpty()) { + return componentInfo; + } + boolean issueExisting = false; + List possibleIssues = Arrays.asList("LicenseRef-scancode-free-unknown"); + for (LicenseInfo li : licenses) { + for (String key : possibleIssues) { + if (key.equals(li.getSpdxid())) { + issueExisting = true; + break; + } + } + if (issueExisting) + break; + } + if (issueExisting) { + DefaultComponentInfoImpl result = new DefaultComponentInfoImpl(componentInfo); + result.setDataStatus(DataStatusValue.WITH_ISSUES); + return result; + } else { + return componentInfo; + } + } + /** * Returns if the adapter should be active. Default implementation returns true. Subclasses might * override this to enable/disable this functionality depending on some configuration; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java index 75ad03c9..990d45bb 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java @@ -73,9 +73,12 @@ public void setRepoBasePath(String repoBasePath) { * @param packageUrl the identifier for the package * @return the raw data base on scancode and supplemental data. null if no data is available. * @throws ComponentInfoAdapterException is something unexpected happens + * @throws ScancodeProcessingFailedException if no data can be provided due to previous issues when + * downloading/scanning the package data. */ @Override - public ScancodeRawComponentInfo readScancodeData(String packageUrl) throws ComponentInfoAdapterException { + public ScancodeRawComponentInfo readScancodeData(String packageUrl) + throws ComponentInfoAdapterException, ScancodeProcessingFailedException { String packagePathPart = this.packageURLHandler.pathFor(packageUrl); String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json"; @@ -83,6 +86,7 @@ public ScancodeRawComponentInfo readScancodeData(String packageUrl) throws Compo File scanCodeFile = new File(path); if (!scanCodeFile.exists()) { LOG.debug("No Scancode info available for PackageURL '{}'", packageUrl); + throwExceptionForDownloadOrScanningFailures(packagePathPart); return null; } String scancodeString; @@ -99,6 +103,30 @@ public ScancodeRawComponentInfo readScancodeData(String packageUrl) throws Compo return result; } + /** + * Check if the unavailability of data is caused by previous failures when downloading of scanning the package + * sources. This is done by testing if the marker files which indicate such failures exist. + * + * @param packagePathPart part of the file path to the data + * @throws ScancodeProcessingFailedException if failures when downloading or scanning were detected + */ + private void throwExceptionForDownloadOrScanningFailures(String packagePathPart) + throws ScancodeProcessingFailedException { + + // Check if "sources.failed" exists + String sourcesFailedPath = this.repoBasePath + "/" + packagePathPart + "/sources.failed"; + File sourcesFailedFile = new File(sourcesFailedPath); + if (sourcesFailedFile.exists()) { + throw new ScancodeProcessingFailedException("Downloading of package sources had failed."); + } + // Check if "scancodeScan.failed" exists + String scancodeScanFailedPath = this.repoBasePath + "/" + packagePathPart + "/scancodeScan.failed"; + File scancodeScanFailedFile = new File(scancodeScanFailedPath); + if (scancodeScanFailedFile.exists()) { + throw new ScancodeProcessingFailedException("Scanning of package sources had failed."); + } + } + /** * Adds the data about the origin of the package which is (optionally) contained in file "origin.yaml" * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java index 59017573..a84ad589 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java @@ -12,10 +12,14 @@ import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.DataStatusValue; +import com.devonfw.tools.solicitor.componentinfo.DefaultComponentInfoImpl; import com.devonfw.tools.solicitor.componentinfo.curation.CurationProvider; import com.devonfw.tools.solicitor.componentinfo.curation.FilteredComponentInfoProvider; import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; +import com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeComponentInfo.ScancodeComponentInfoData; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -91,25 +95,31 @@ public void setMinLicensefileNumberOfLines(int minLicensefileNumberOfLines) { * @param packageUrl The package url of the package * @param curationDataSelector identifies which source should be used for the curation data. null * indicates that the default should be used. - * @return the read scancode information, null if no information was found + * @return the read scancode information * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be return in such a case. */ @Override - public ScancodeComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) + public ComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) throws ComponentInfoAdapterException { - ScancodeRawComponentInfo rawScancodeData = this.fileScancodeRawComponentInfoProvider.readScancodeData(packageUrl); + ScancodeRawComponentInfo rawScancodeData; + try { + rawScancodeData = this.fileScancodeRawComponentInfoProvider.readScancodeData(packageUrl); + } catch (ScancodeProcessingFailedException e) { + return new DefaultComponentInfoImpl(packageUrl, DataStatusValue.PROCESSING_FAILED); + } if (rawScancodeData == null) { - return null; + return new DefaultComponentInfoImpl(packageUrl, DataStatusValue.NOT_AVAILABLE); } ScancodeComponentInfo componentScancodeInfos = parseAndMapScancodeJson(packageUrl, rawScancodeData, curationDataSelector); addSupplementedData(rawScancodeData, componentScancodeInfos); LOG.debug("Scancode info for package {}: {} license, {} copyrights, {} NOTICE files", packageUrl, - componentScancodeInfos.getLicenses().size(), componentScancodeInfos.getCopyrights().size(), - componentScancodeInfos.getNoticeFileUrl() != null ? 1 : 0); + componentScancodeInfos.getComponentInfoData().getLicenses().size(), + componentScancodeInfos.getComponentInfoData().getCopyrights().size(), + componentScancodeInfos.getComponentInfoData().getNoticeFileUrl() != null ? 1 : 0); return componentScancodeInfos; } @@ -121,8 +131,8 @@ public ScancodeComponentInfo getComponentInfo(String packageUrl, String curation private void addSupplementedData(ScancodeRawComponentInfo rawScancodeData, ScancodeComponentInfo componentScancodeInfos) { - componentScancodeInfos.setSourceDownloadUrl(rawScancodeData.sourceDownloadUrl); - componentScancodeInfos.setPackageDownloadUrl(rawScancodeData.packageDownloadUrl); + componentScancodeInfos.getComponentInfoData().setSourceDownloadUrl(rawScancodeData.sourceDownloadUrl); + componentScancodeInfos.getComponentInfoData().setPackageDownloadUrl(rawScancodeData.packageDownloadUrl); } /** @@ -137,6 +147,11 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore, this.minLicensefileNumberOfLines); componentScancodeInfos.setPackageUrl(packageUrl); + // set status to NO_ISSUES. This might be overridden later if issues are detected + componentScancodeInfos.setDataStatus(DataStatusValue.NO_ISSUES); + + // get the object which hold the actual data + ScancodeComponentInfoData scancodeComponentInfoData = componentScancodeInfos.getComponentInfoData(); // Get the curation for a given packageUrl ComponentInfoCuration componentInfoCuration = this.curationProvider.findCurations(packageUrl, curationDataSelector); @@ -164,16 +179,16 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod continue; } if (path.contains("/NOTICE")) { - componentScancodeInfos + scancodeComponentInfoData .addNoticeFileUrl(this.fileScancodeRawComponentInfoProvider.pkgContentUriFromPath(packageUrl, path), 100.0); } double licenseTextRatio = file.get("percentage_of_license_text").asDouble(); boolean takeCompleteFile = licenseTextRatio >= this.licenseToTextRatioToTakeCompleteFile; for (JsonNode cr : file.get("copyrights")) { if (cr.has("copyright")) { - componentScancodeInfos.addCopyright(cr.get("copyright").asText()); + scancodeComponentInfoData.addCopyright(cr.get("copyright").asText()); } else { - componentScancodeInfos.addCopyright(cr.get("value").asText()); + scancodeComponentInfoData.addCopyright(cr.get("value").asText()); } } @@ -241,13 +256,13 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod givenLicenseText = this.fileScancodeRawComponentInfoProvider.retrieveContent(packageUrl, licenseUrl); } - componentScancodeInfos.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseUrl, + scancodeComponentInfoData.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseUrl, givenLicenseText, endLine - startLine); } } - if (componentScancodeInfos.getNoticeFileUrl() != null) { - componentScancodeInfos.setNoticeFileContent(this.fileScancodeRawComponentInfoProvider.retrieveContent(packageUrl, - componentScancodeInfos.getNoticeFileUrl())); + if (scancodeComponentInfoData.getNoticeFileUrl() != null) { + scancodeComponentInfoData.setNoticeFileContent(this.fileScancodeRawComponentInfoProvider + .retrieveContent(packageUrl, scancodeComponentInfoData.getNoticeFileUrl())); } return componentScancodeInfos; } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index 80038865..c8b8923f 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -10,6 +10,7 @@ import java.util.TreeSet; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoData; import com.devonfw.tools.solicitor.componentinfo.LicenseInfo; /** @@ -62,15 +63,16 @@ public class ScancodeLicenseInfo implements LicenseInfo { * @param licenseUrl the path to the license file * @param givenLicenseText the given license text (might be null) * @param licenseFileScore the score of the license file - number of lines with license info + * @param minLicensefileNumberOfLines the minimum number of lines to accept the given text as license text */ public ScancodeLicenseInfo(String id, String spdxid, String defaultUrl, double licenseScore, String licenseUrl, - String givenLicenseText, int licenseFileScore) { + String givenLicenseText, int licenseFileScore, int minLicensefileNumberOfLines) { super(); this.id = id; this.spdxid = spdxid; this.licenseScore = licenseScore; - if (licenseFileScore >= ScancodeComponentInfo.this.minLicensefileNumberOfLines) { + if (licenseFileScore >= minLicensefileNumberOfLines) { this.licenseUrl = licenseUrl; this.licenseFileScore = licenseFileScore; this.givenLicenseText = givenLicenseText; @@ -181,257 +183,276 @@ public void setLicenseFileScore(int licenseFileScore) { } - private static final double MIN_NOTICEFILE_PERCENTAGE = 0.0; + public class ScancodeComponentInfoData implements ComponentInfoData { + private SortedSet copyrights = new TreeSet<>(); - private SortedSet copyrights = new TreeSet<>(); + private SortedMap licenses = new TreeMap<>(); - private SortedMap licenses = new TreeMap<>(); + private String noticeFileUrl = null; - private String packageUrl; + private String noticeFileContent; - private double noticeFileScore = 0; + private String url; - private String noticeFileUrl = null; + private String sourceRepoUrl; - private String noticeFileContent; + private String packageDownloadUrl; - private String url; + private String sourceDownloadUrl; - private String sourceRepoUrl; + private double noticeFileScore = 0; - private int minLicensefileNumberOfLines; + private int minLicensefileNumberOfLines; - private double minLicenseScore; + private double minLicenseScore; - private String packageDownloadUrl; + /** + * The constructor. + * + * @param minLicenseScore + * @param minLicensefileNumberOfLines + */ + public ScancodeComponentInfoData(double minLicenseScore, int minLicensefileNumberOfLines) { - private String sourceDownloadUrl; + super(); + this.minLicenseScore = minLicenseScore; + this.minLicensefileNumberOfLines = minLicensefileNumberOfLines; - private String dataStatus; + } - private List traceabilityNotes = new ArrayList<>(); + /** + * Adds a single copyright line. + * + * @param copyright the copyright + */ + public void addCopyright(String copyright) { - /** - * The constructor. - * - * @param minLicenseScore the minimum score to take license findings into account - * @param minLicensefileNumberOfLines the minimum number of licens text lines to possibly use file as license file - */ - public ScancodeComponentInfo(double minLicenseScore, int minLicensefileNumberOfLines) { + this.copyrights.add(copyright); + } - super(); - this.minLicenseScore = minLicenseScore; - this.minLicensefileNumberOfLines = minLicensefileNumberOfLines; - } + /** + * Gets all copyrights. + * + * @return the copyrights + */ + @Override + public Collection getCopyrights() { - /** - * Adds a single copyright line. - * - * @param copyright the copyright - */ - public void addCopyright(String copyright) { + return Collections.unmodifiableCollection(this.copyrights); + } - this.copyrights.add(copyright); - } + /** + * Clears all found copyrights. + */ + public void clearCopyrights() { - /** - * Gets all copyrights. - * - * @return the copyrights - */ - @Override - public Collection getCopyrights() { + this.copyrights = new TreeSet<>(); + } - return Collections.unmodifiableCollection(this.copyrights); - } + /** + * Adds a license or updates the information if the relevant scores exceed the required thresholds and the score is + * better than the score of already existing information. + * + * @param licenseId the license id + * @param licenseName the name of the license (SPDS-ID) + * @param licenseDefaultUrl the url of the generic license text + * @param score the score of the license finding + * @param filePath path to the license file + * @param givenLicenseText the license text + * @param fileScore score of the license file - measured as number of lines which were detected as license text + */ + public void addLicense(String licenseId, String licenseName, String licenseDefaultUrl, double score, + String filePath, String givenLicenseText, int fileScore) { + + if (this.licenses.containsKey(licenseId)) { + ScancodeLicenseInfo existingLicenseInfo = this.licenses.get(licenseId); + + double resultingScore = Math.max(existingLicenseInfo.getLicenseScore(), score); + String resultingFilePath = existingLicenseInfo.getLicenseUrl(); + String resultingGivenText = existingLicenseInfo.getGivenLicenseText(); + int resultingFileScore = existingLicenseInfo.getLicenseFileScore(); + if (fileScore > existingLicenseInfo.getLicenseFileScore()) { + resultingFilePath = filePath; + resultingFileScore = fileScore; + resultingGivenText = givenLicenseText; + } + this.licenses.put(licenseId, new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, resultingScore, + resultingFilePath, resultingGivenText, resultingFileScore, this.minLicensefileNumberOfLines)); - /** - * Clears all found copyrights. - */ - public void clearCopyrights() { + } else { + if (score >= this.minLicenseScore) { + this.licenses.put(licenseId, new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, + filePath, givenLicenseText, fileScore, this.minLicensefileNumberOfLines)); + } + } + } - this.copyrights = new TreeSet<>(); - } + /** + * Gets all licenses. + * + * @return all licenses + */ + @Override + public Collection getLicenses() { - /** - * Adds a license or updates the information if the relevant scores exceed the required thresholds and the score is - * better than the score of already existing information. - * - * @param licenseId the license id - * @param licenseName the name of the license (SPDS-ID) - * @param licenseDefaultUrl the url of the generic license text - * @param score the score of the license finding - * @param filePath path to the license file - * @param givenLicenseText the license text - * @param fileScore score of the license file - measured as number of lines which were detected as license text - */ - public void addLicense(String licenseId, String licenseName, String licenseDefaultUrl, double score, String filePath, - String givenLicenseText, int fileScore) { - - if (this.licenses.containsKey(licenseId)) { - ScancodeLicenseInfo existingLicenseInfo = this.licenses.get(licenseId); - - double resultingScore = Math.max(existingLicenseInfo.getLicenseScore(), score); - String resultingFilePath = existingLicenseInfo.getLicenseUrl(); - String resultingGivenText = existingLicenseInfo.getGivenLicenseText(); - int resultingFileScore = existingLicenseInfo.getLicenseFileScore(); - if (fileScore > existingLicenseInfo.getLicenseFileScore()) { - resultingFilePath = filePath; - resultingFileScore = fileScore; - resultingGivenText = givenLicenseText; - } - this.licenses.put(licenseId, new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, resultingScore, - resultingFilePath, resultingGivenText, resultingFileScore)); + return Collections.unmodifiableSortedMap(this.licenses).values(); + } - } else { - if (score >= this.minLicenseScore) { - this.licenses.put(licenseId, new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, filePath, - givenLicenseText, fileScore)); + /** + * Clears all stored licenses. + */ + public void clearLicenses() { + + this.licenses = new TreeMap<>(); + } + + /** + * Stores the reference to a NOTICE file if the score exceeds the minimum required score and is higher than the + * score of already existing information. + * + * @param url reference to the NOTICE file + * @param score score of the file + */ + public void addNoticeFileUrl(String url, double score) { + + if (score > this.noticeFileScore && score >= MIN_NOTICEFILE_PERCENTAGE) { + this.noticeFileUrl = url; + this.noticeFileScore = score; } } - } - /** - * Gets all licenses. - * - * @return all licenses - */ - @Override - public Collection getLicenses() { + /** + * @param noticeFileContent new value of {@link #getNoticeFileContent}. + */ + public void setNoticeFileContent(String noticeFileContent) { - return Collections.unmodifiableSortedMap(this.licenses).values(); - } + this.noticeFileContent = noticeFileContent; + } - /** - * Clears all stored licenses. - */ - public void clearLicenses() { + /** + * Gets the referennce to the notice file (if any). + * + * @return reference to the notice file + */ + @Override + public String getNoticeFileUrl() { - this.licenses = new TreeMap<>(); - } + return this.noticeFileUrl; + } - /** - * Stores the reference to a NOTICE file if the score exceeds the minimum required score and is higher than the score - * of already existing information. - * - * @param url reference to the NOTICE file - * @param score score of the file - */ - public void addNoticeFileUrl(String url, double score) { + @Override + public String getNoticeFileContent() { - if (score > this.noticeFileScore && score >= MIN_NOTICEFILE_PERCENTAGE) { - this.noticeFileUrl = url; - this.noticeFileScore = score; + return this.noticeFileContent; } - } - /** - * @param noticeFileContent new value of {@link #getNoticeFileContent}. - */ - public void setNoticeFileContent(String noticeFileContent) { + /** + * Gets the url of the projects homepage. + * + * @return url to the projects homepage + */ + @Override + public String getHomepageUrl() { - this.noticeFileContent = noticeFileContent; - } + return this.url; + } - /** - * Gets the referennce to the notice file (if any). - * - * @return reference to the notice file - */ - @Override - public String getNoticeFileUrl() { + /** + * Sets the url of the projects homepage. + * + * @param url new value of {@link #getHomepageUrl()}. + */ + public void setHomepageUrl(String url) { - return this.noticeFileUrl; - } + this.url = url; + } - @Override - public String getNoticeFileContent() { + /** + * Gets the url of the source code repository. + * + * @return sourceRepoUrl to the license text + */ + @Override + public String getSourceRepoUrl() { - return this.noticeFileContent; - } + return this.sourceRepoUrl; + } - /** - * Gets the url of the projects homepage. - * - * @return url to the projects homepage - */ - @Override - public String getHomepageUrl() { + /** + * Sets the url of the source code repository. + * + * @param sourceRepoUrl new value of {@link #getSourceRepoUrl()}. + */ + public void setSourceRepoUrl(String sourceRepoUrl) { - return this.url; - } + this.sourceRepoUrl = sourceRepoUrl; + } - /** - * Sets the url of the projects homepage. - * - * @param url new value of {@link #getHomepageUrl()}. - */ - public void setHomepageUrl(String url) { + /** + * Gets the url to download the package. + * + * @return url to download the package + */ + @Override + public String getPackageDownloadUrl() { - this.url = url; - } + return this.packageDownloadUrl; + } - /** - * Gets the url of the source code repository. - * - * @return sourceRepoUrl to the license text - */ - @Override - public String getSourceRepoUrl() { + /** + * Sets the url to download the package. + * + * @param packageDownloadUrl new value of {@link #getPackageDownloadUrl()}. + */ + public void setPackageDownloadUrl(String packageDownloadUrl) { - return this.sourceRepoUrl; - } + this.packageDownloadUrl = packageDownloadUrl; + } - /** - * Sets the url of the source code repository. - * - * @param sourceRepoUrl new value of {@link #getSourceRepoUrl()}. - */ - public void setSourceRepoUrl(String sourceRepoUrl) { + /** + * Gets the url to download the sources of the package. + * + * @return url to download the sources of the package + */ + @Override + public String getSourceDownloadUrl() { - this.sourceRepoUrl = sourceRepoUrl; - } + return this.sourceDownloadUrl; + } - /** - * Gets the url to download the package. - * - * @return url to download the package - */ - @Override - public String getPackageDownloadUrl() { + /** + * Sets the url to download the sources of the package. + * + * @param sourceDownloadUrl new value of {@link #getSourceDownloadUrl()}. + */ + public void setSourceDownloadUrl(String sourceDownloadUrl) { + + this.sourceDownloadUrl = sourceDownloadUrl; + } - return this.packageDownloadUrl; } - /** - * Sets the url to download the package. - * - * @param packageDownloadUrl new value of {@link #getPackageDownloadUrl()}. - */ - public void setPackageDownloadUrl(String packageDownloadUrl) { + private static final double MIN_NOTICEFILE_PERCENTAGE = 0.0; - this.packageDownloadUrl = packageDownloadUrl; - } + private ScancodeComponentInfoData scancodeComponentInfoData; - /** - * Gets the url to download the sources of the package. - * - * @return url to download the sources of the package - */ - @Override - public String getSourceDownloadUrl() { + private String packageUrl; - return this.sourceDownloadUrl; - } + private String dataStatus; + + private List traceabilityNotes = new ArrayList<>(); /** - * Sets the url to download the sources of the package. + * The constructor. * - * @param sourceDownloadUrl new value of {@link #getSourceDownloadUrl()}. + * @param minLicenseScore the minimum score to take license findings into account + * @param minLicensefileNumberOfLines the minimum number of licens text lines to possibly use file as license file */ - public void setSourceDownloadUrl(String sourceDownloadUrl) { + public ScancodeComponentInfo(double minLicenseScore, int minLicensefileNumberOfLines) { + + super(); + this.scancodeComponentInfoData = new ScancodeComponentInfoData(minLicenseScore, minLicensefileNumberOfLines); - this.sourceDownloadUrl = sourceDownloadUrl; } /** @@ -470,6 +491,15 @@ public void setTraceabilityNotes(List traceabilityNotes) { this.traceabilityNotes = traceabilityNotes; } + /** + * @return scancodeComponentInfoData + */ + @Override + public ScancodeComponentInfoData getComponentInfoData() { + + return this.scancodeComponentInfoData; + } + @Override public String getPackageUrl() { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeProcessingFailedException.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeProcessingFailedException.java new file mode 100644 index 00000000..231b7aa7 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeProcessingFailedException.java @@ -0,0 +1,37 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +/** + * Exception indicating that the {@link ScancodeRawComponentInfoProvider} could not provide any data due to previous + * failures in (asynchronous) downloading or scanning of the package data. + */ +public class ScancodeProcessingFailedException extends Exception { + + /** + * A constructor. + */ + public ScancodeProcessingFailedException() { + + } + + /** + * A constructor. + * + * @param message the message of the exception + */ + public ScancodeProcessingFailedException(String message) { + + super(message); + } + + /** + * A constructor. + * + * @param message the message of the exception + * @param cause the underlying {@link Throwable}. + */ + public ScancodeProcessingFailedException(String message, Throwable cause) { + + super(message, cause); + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoProvider.java index 46725130..9d0fd6db 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeRawComponentInfoProvider.java @@ -15,8 +15,11 @@ public interface ScancodeRawComponentInfoProvider extends ComponentContentProvid * @param packageUrl the identifier for the package * @return the raw data based on scancode and supplemental data. null if no data is available. * @throws ComponentInfoAdapterException is something unexpected happens + * @throws ScancodeProcessingFailedException if no data can be provided due to previous issues when + * downloading/scanning the package data. */ - ScancodeRawComponentInfo readScancodeData(String packageUrl) throws ComponentInfoAdapterException; + ScancodeRawComponentInfo readScancodeData(String packageUrl) + throws ComponentInfoAdapterException, ScancodeProcessingFailedException; /** * Creates a pkgcontent-URI (see {@link ComponentContentProvider}) from the relative local file path. diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java index 59fdb2a5..0ee9d141 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java @@ -9,6 +9,7 @@ import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; @@ -58,17 +59,17 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/nonexisting.yaml"); // when - ScancodeComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); // then - assertNotNull(scancodeComponentInfo); + assertNotNull(scancodeComponentInfo.getComponentInfoData()); assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", scancodeComponentInfo.getPackageUrl()); assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", - scancodeComponentInfo.getNoticeFileContent()); - assertEquals(1, scancodeComponentInfo.getCopyrights().size()); - assertEquals("Copyright 2023 devonfw", scancodeComponentInfo.getCopyrights().toArray()[0]); + scancodeComponentInfo.getComponentInfoData().getNoticeFileContent()); + assertEquals(1, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + assertEquals("Copyright 2023 devonfw", scancodeComponentInfo.getComponentInfoData().getCopyrights().toArray()[0]); } /** @@ -84,17 +85,18 @@ public void testGetComponentInfoWithCurationsAndExclusions() throws ComponentInf this.singleFileCurationProvider .setCurationsFileName("src/test/resources/scancodefileadapter/curations_with_exclusions.yaml"); // when - ScancodeComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); // then - assertNotNull(scancodeComponentInfo); + assertNotNull(scancodeComponentInfo.getComponentInfoData()); assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", scancodeComponentInfo.getPackageUrl()); assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", - scancodeComponentInfo.getNoticeFileContent()); - assertEquals(0, scancodeComponentInfo.getCopyrights().size()); // since the copyright is found under - // /src/../SampleClass.java1, it will be excluded + scancodeComponentInfo.getComponentInfoData().getNoticeFileContent()); + assertEquals(0, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); // since the copyright is + // found under + // /src/../SampleClass.java1, it will be excluded } /** @@ -110,20 +112,21 @@ public void testGetComponentInfoWithCurationsAndWithoutExclusions() throws Compo this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/curations.yaml"); // when - ScancodeComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); // then - assertNotNull(scancodeComponentInfo); + assertNotNull(scancodeComponentInfo.getComponentInfoData()); assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", scancodeComponentInfo.getPackageUrl()); assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", - scancodeComponentInfo.getNoticeFileContent()); - assertEquals(1, scancodeComponentInfo.getCopyrights().size()); - assertEquals("Copyright 2023 devonfw", scancodeComponentInfo.getCopyrights().toArray()[0]); // The copyright - // curation does not - // apply on the - // scancodeComponentInfo - // object. + scancodeComponentInfo.getComponentInfoData().getNoticeFileContent()); + assertEquals(1, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + assertEquals("Copyright 2023 devonfw", scancodeComponentInfo.getComponentInfoData().getCopyrights().toArray()[0]); // The + // copyright + // curation does not + // apply on the + // scancodeComponentInfo + // object. } } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java index a090f35e..6d62b088 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java @@ -84,7 +84,7 @@ public void testGetComponentInfoNaPackage() throws ComponentInfoAdapterException .getComponentInfo("pkg:maven/com.devonfw.tools/unknown@0.1.0", "someCurationSelector"); // then - assertNull(componentInfo); + assertNull(componentInfo.getComponentInfoData()); } /** @@ -104,18 +104,18 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); // then - assertNotNull(componentInfo); + assertNotNull(componentInfo.getComponentInfoData()); assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", componentInfo.getPackageUrl()); assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", - componentInfo.getNoticeFileContent()); - assertEquals("pkgcontent:/NOTICE.txt", componentInfo.getNoticeFileUrl()); - assertEquals(1, componentInfo.getCopyrights().size()); - assertEquals("Copyright 2023 devonfw", componentInfo.getCopyrights().toArray()[0]); - assertEquals(2, componentInfo.getLicenses().size()); + componentInfo.getComponentInfoData().getNoticeFileContent()); + assertEquals("pkgcontent:/NOTICE.txt", componentInfo.getComponentInfoData().getNoticeFileUrl()); + assertEquals(1, componentInfo.getComponentInfoData().getCopyrights().size()); + assertEquals("Copyright 2023 devonfw", componentInfo.getComponentInfoData().getCopyrights().toArray()[0]); + assertEquals(2, componentInfo.getComponentInfoData().getLicenses().size()); boolean apacheFound = false; boolean unknownFound = false; - for (LicenseInfo li : componentInfo.getLicenses()) { + for (LicenseInfo li : componentInfo.getComponentInfoData().getLicenses()) { if (li.getSpdxid().equals("Apache-2.0")) { Assertions.assertTrue(li.getGivenLicenseText().contains("Unless required by applicable")); apacheFound = true; @@ -128,11 +128,11 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx assertTrue(apacheFound && unknownFound); assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", - componentInfo.getNoticeFileContent()); + componentInfo.getComponentInfoData().getNoticeFileContent()); assertEquals("https://somehost/test-project-for-deep-license-scan-0.1.0.jar", - componentInfo.getPackageDownloadUrl()); + componentInfo.getComponentInfoData().getPackageDownloadUrl()); assertEquals("https://somehost/test-project-for-deep-license-scan-0.1.0-sources.jar", - componentInfo.getSourceDownloadUrl()); + componentInfo.getComponentInfoData().getSourceDownloadUrl()); } @@ -160,7 +160,7 @@ public void testGetComponentCheckCurationDataSelector() throws ComponentInfoAdap "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); // then - assertNotNull(componentInfo); + assertNotNull(componentInfo.getComponentInfoData()); ArgumentCaptor captor1 = ArgumentCaptor.forClass(String.class); ArgumentCaptor captor2 = ArgumentCaptor.forClass(String.class); @@ -182,17 +182,17 @@ public void testGetComponentInfoWithCurations() throws ComponentInfoAdapterExcep "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); // then - assertNotNull(componentInfo); + assertNotNull(componentInfo.getComponentInfoData()); assertEquals("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", componentInfo.getPackageUrl()); assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", - componentInfo.getNoticeFileContent()); - assertEquals("pkgcontent:/NOTICE.txt", componentInfo.getNoticeFileUrl()); - assertEquals(1, componentInfo.getCopyrights().size()); - assertEquals("Copyright (c) 2023 somebody", componentInfo.getCopyrights().toArray()[0]); - assertEquals(1, componentInfo.getLicenses().size()); + componentInfo.getComponentInfoData().getNoticeFileContent()); + assertEquals("pkgcontent:/NOTICE.txt", componentInfo.getComponentInfoData().getNoticeFileUrl()); + assertEquals(1, componentInfo.getComponentInfoData().getCopyrights().size()); + assertEquals("Copyright (c) 2023 somebody", componentInfo.getComponentInfoData().getCopyrights().toArray()[0]); + assertEquals(1, componentInfo.getComponentInfoData().getLicenses().size()); boolean mitFound = false; - for (LicenseInfo li : componentInfo.getLicenses()) { + for (LicenseInfo li : componentInfo.getComponentInfoData().getLicenses()) { if (li.getSpdxid().equals("MIT")) { Assertions.assertEquals("https://some/license/url", li.getLicenseUrl()); mitFound = true; @@ -201,12 +201,12 @@ public void testGetComponentInfoWithCurations() throws ComponentInfoAdapterExcep assertTrue(mitFound); assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", - componentInfo.getNoticeFileContent()); + componentInfo.getComponentInfoData().getNoticeFileContent()); assertEquals("https://somehost/test-project-for-deep-license-scan-0.1.0.jar", - componentInfo.getPackageDownloadUrl()); + componentInfo.getComponentInfoData().getPackageDownloadUrl()); assertEquals("https://somehost/test-project-for-deep-license-scan-0.1.0-sources.jar", - componentInfo.getSourceDownloadUrl()); - assertEquals("http://some/url", componentInfo.getSourceRepoUrl()); + componentInfo.getComponentInfoData().getSourceDownloadUrl()); + assertEquals("http://some/url", componentInfo.getComponentInfoData().getSourceRepoUrl()); } } diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index bf70422b..c571a3f5 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -155,7 +155,7 @@ The internal business data model consists of 6 entities: | copyrights | String | Copyright statements found in the components metadata / code (optional, see <>) | packageDownloadUrl | String | URL for downloading the component (optional, see <>) | sourceDownloadUrl | String | URL for downloading the sources of the component (optional, see <>) -| dataStatus | String | Optional status of the data associated with the component. Possible values TBD. +| dataStatus | String | Optional status of the data associated with the component. See <> for values used by the Scancode integration. Extensions (see <>) might use different values. | traceabilityNotes | String | Optional notes for tracing the information about this component back to its origin. |=== @@ -1427,6 +1427,21 @@ Main target of the additional information obtained from ScanCode is currently th * including all different license texts * and contents of all found NOTICE files +==== dataStatus values of the Scancode integration +When using the Scancode integration the following values are used for field `ApplicationComponent.dataStatus`: + + +[width="100%",cols="1,3",options="header"] +|==================== +| Value | Description +| `ND:DISABLED` | No data available. Scancode integration disabled. +| `ND:NOT_AVAILABLE` | No data available. No scan results existing and no indication that attempting that download/scanning has failed. +| `ND:PROCESSING_FAILED` | No data available. No scan results existing. Processing (downloading or scanning) had failed. +| `DA:WITH_ISSUES` | Data available. Issues were detected in the data which probably need to be curated. +| `DA:NO_ISSUES` | Data available. No curations applied. No issues were detected. +| `DA:CURATED` | Data available. Curations were applied. No issues were detected. +|==================== + === Correcting data The data obtained from ScanCode might be affected by false positives (wrongly detected a license or copyright) or false negatives (missed to detect a license or copyright). To compensate such defects there are two mechanisms: @@ -1621,6 +1636,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.17.0:: +* https://github.com/devonfw/solicitor/issues/221: Provide status information on data obtained from Scancode in field `ApplicationComponent.dataStatus`. See <>. Changes in 1.16.0:: * https://github.com/devonfw/solicitor/pull/212: Improvement in determining License-URL within NpmLicenseCheckerReader. From e8e63024e22fa0e6c3b8f4ddb2737651bb94d636 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:44:50 +0100 Subject: [PATCH 086/139] Minor documentation fix --- documentation/master-solicitor.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index c571a3f5..542e1b5d 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1435,7 +1435,7 @@ When using the Scancode integration the following values are used for field `App |==================== | Value | Description | `ND:DISABLED` | No data available. Scancode integration disabled. -| `ND:NOT_AVAILABLE` | No data available. No scan results existing and no indication that attempting that download/scanning has failed. +| `ND:NOT_AVAILABLE` | No data available. No scan results existing and no indication that attempting download/scanning has failed. | `ND:PROCESSING_FAILED` | No data available. No scan results existing. Processing (downloading or scanning) had failed. | `DA:WITH_ISSUES` | Data available. Issues were detected in the data which probably need to be curated. | `DA:NO_ISSUES` | Data available. No curations applied. No issues were detected. From 885d11aceb9c136d26217443dab46c458f70f667 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 15 Jan 2024 11:43:14 +0100 Subject: [PATCH 087/139] Version 1.17.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 3cb23395..573787e0 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.17.0-SNAPSHOT + 1.17.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index a82163b2..b8a156c5 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.17.0-SNAPSHOT + 1.17.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index c748ba46..199f1491 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.17.0-SNAPSHOT + 1.17.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index ec01bc2c..f323afda 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.17.0-SNAPSHOT + 1.17.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index ef089c0e..564b3943 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.17.0-SNAPSHOT + 1.17.0 pom Solicitor Aggregator From 9008a1b1fd55e30936634d7da0ec5a27ec5fba71 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 15 Jan 2024 11:53:43 +0100 Subject: [PATCH 088/139] Set version to SNAPSHOT of next minor release --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 573787e0..e71a3fc7 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.17.0 + 1.18.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index b8a156c5..0a310e75 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.17.0 + 1.18.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 199f1491..8cd027cf 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.17.0 + 1.18.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index f323afda..c81fafab 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.17.0 + 1.18.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 564b3943..0e22b7f8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.17.0 + 1.18.0-SNAPSHOT pom Solicitor Aggregator From 193edd9d589e5eaf7f7a769e82b2f6bd98acd5d0 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:41:02 +0100 Subject: [PATCH 089/139] Prepare releasenotes for upcoming version --- documentation/master-solicitor.asciidoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 542e1b5d..91b33574 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1635,6 +1635,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.18.0:: + Changes in 1.17.0:: * https://github.com/devonfw/solicitor/issues/221: Provide status information on data obtained from Scancode in field `ApplicationComponent.dataStatus`. See <>. From 6045bfa80ba9dba39691a4663c18f0402f62a845 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:13:11 +0100 Subject: [PATCH 090/139] Fix for #223 (broken command line parsing) (#224) * Added test for reproducing https://github.com/devonfw/solicitor/issues/223 (cherry picked from commit 97346107790d5c952c21a74d3612f50d06f2e698) * Fix of broken command line parsing (#223) (cherry picked from commit 55dd2173682fa9a33f88828620feab23b990e716) --- .../solicitor/SolicitorCliProcessor.java | 4 +- .../solicitor/SolicitorCliProcessorTest.java | 173 ++++++++++++++++++ documentation/master-solicitor.asciidoc | 9 +- 3 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/SolicitorCliProcessorTest.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/SolicitorCliProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/SolicitorCliProcessor.java index e64e1bcd..f66f7a4e 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/SolicitorCliProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/SolicitorCliProcessor.java @@ -122,7 +122,6 @@ public CommandLineOptions parse(String[] commandLineArgs) { builder = Option.builder("l"); builder.longOpt("loadModel"); builder.hasArg(); - builder.optionalArg(false); builder.argName("filename"); description = "instead of reading raw license data and processing rules load the already " + "processed model from a previously saved file"; @@ -134,7 +133,6 @@ public CommandLineOptions parse(String[] commandLineArgs) { builder = Option.builder("d"); builder.longOpt("diff"); builder.hasArg(); - builder.optionalArg(false); builder.argName("filename"); description = "create a diff report to the already processed model given by this filename"; builder.desc(description); @@ -204,7 +202,7 @@ public CommandLineOptions parse(String[] commandLineArgs) { /** * Prints help information to standard output. - * + * * @param options the {@link Options} object which describes the command line syntax */ private void printHelp(Options options) { diff --git a/core/src/test/java/com/devonfw/tools/solicitor/SolicitorCliProcessorTest.java b/core/src/test/java/com/devonfw/tools/solicitor/SolicitorCliProcessorTest.java new file mode 100644 index 00000000..968a68ee --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/SolicitorCliProcessorTest.java @@ -0,0 +1,173 @@ +package com.devonfw.tools.solicitor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.solicitor.SolicitorCliProcessor.CommandLineOptions; + +/** + * Tests for class {@link SolicitorCliProcessor}. + * + */ +class SolicitorCliProcessorTest { + + @Test + void testCliParsingHelpOption() { + + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + String[] testCli = new String[] { "-eug" }; + CommandLineOptions clo = scp.parse(testCli); + assertFalse(clo.help); + + testCli = new String[] { "-h" }; + clo = scp.parse(testCli); + assertTrue(clo.help); + + testCli = new String[] { "--help" }; + clo = scp.parse(testCli); + assertTrue(clo.help); + } + + @Test + void testCliParsingEugOption() { + + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + String[] testCli = new String[] { "-h" }; + CommandLineOptions clo = scp.parse(testCli); + assertFalse(clo.extractUserGuide); + + testCli = new String[] { "-eug" }; + clo = scp.parse(testCli); + assertTrue(clo.extractUserGuide); + + testCli = new String[] { "--extractUserGuide" }; + clo = scp.parse(testCli); + assertTrue(clo.extractUserGuide); + } + + @Test + void testCliParsingWizOption() { + + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + String[] testCli = new String[] { "-h" }; + CommandLineOptions clo = scp.parse(testCli); + assertFalse(clo.wizard); + + testCli = new String[] { "-wiz", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.wizard); + assertEquals("arg", clo.targetDir); + + testCli = new String[] { "--projectWizard", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.wizard); + assertEquals("arg", clo.targetDir); + } + + @Test + void testCliParsingEcOption() { + + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + String[] testCli = new String[] { "-h" }; + CommandLineOptions clo = scp.parse(testCli); + assertFalse(clo.extractConfig); + + testCli = new String[] { "-ec", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.extractConfig); + assertEquals("arg", clo.targetDir); + + testCli = new String[] { "--extractConfig", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.extractConfig); + assertEquals("arg", clo.targetDir); + } + + @Test + void testCliParsingConfigOption() { + + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + String[] testCli = new String[] { "-h" }; + CommandLineOptions clo = scp.parse(testCli); + assertNull(clo.configUrl); + + testCli = new String[] { "-c", "arg" }; + clo = scp.parse(testCli); + assertEquals("arg", clo.configUrl); + + testCli = new String[] { "--config"/* , "arg" */ }; + // clo = scp.parse(testCli); + // assertEquals("arg", clo.configUrl); + } + + @Test + void testCliParsingSaveModelOption() { + + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + String[] testCli = new String[] { "-h" }; + CommandLineOptions clo = scp.parse(testCli); + assertFalse(clo.save); + + testCli = new String[] { "-s", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.save); + assertEquals("arg", clo.pathForSave); + + testCli = new String[] { "--saveModel", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.save); + assertEquals("arg", clo.pathForSave); + + testCli = new String[] { "-s" }; + clo = scp.parse(testCli); + assertTrue(clo.save); + assertNull(clo.pathForSave); + + testCli = new String[] { "--saveModel" }; + clo = scp.parse(testCli); + assertTrue(clo.save); + assertNull(clo.pathForSave); + } + + @Test + void testCliParsingLoadModelOption() { + + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + String[] testCli = new String[] { "-h" }; + CommandLineOptions clo = scp.parse(testCli); + assertFalse(clo.load); + + testCli = new String[] { "-l", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.load); + assertEquals("arg", clo.pathForLoad); + + testCli = new String[] { "--loadModel", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.load); + assertEquals("arg", clo.pathForLoad); + } + + @Test + void testCliParsingDiffOption() { + + SolicitorCliProcessor scp = new SolicitorCliProcessor(); + String[] testCli = new String[] { "-h" }; + CommandLineOptions clo = scp.parse(testCli); + assertFalse(clo.diff); + + testCli = new String[] { "-d", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.diff); + assertEquals("arg", clo.pathForDiff); + + testCli = new String[] { "--diff", "arg" }; + clo = scp.parse(testCli); + assertTrue(clo.diff); + assertEquals("arg", clo.pathForDiff); + } +} diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 91b33574..d837536a 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1261,7 +1261,7 @@ The determined content is available as `NormalizedLicense.effectiveNormalizedLic === Encoding of URLs When creating the resource or filename for given URLs in the above steps the following encoding scheme will be applied to ensure that always a valid name can be created: -* If the scheme is `https` it will be replaced with `http`. +* If the scheme is `https` it will be replaced with `http`. * All "non-word" characters (i.e. characters outside the set `[a-zA-Z_0-9]`) are replaced by underscores ("`_`"). * In case that the resulting filename exceeds a length of 250 it will be replaced by a new name concatenated from ** the first 40 characters of the (too) long filename @@ -1433,8 +1433,8 @@ When using the Scancode integration the following values are used for field `App [width="100%",cols="1,3",options="header"] |==================== -| Value | Description -| `ND:DISABLED` | No data available. Scancode integration disabled. +| Value | Description +| `ND:DISABLED` | No data available. Scancode integration disabled. | `ND:NOT_AVAILABLE` | No data available. No scan results existing and no indication that attempting download/scanning has failed. | `ND:PROCESSING_FAILED` | No data available. No scan results existing. Processing (downloading or scanning) had failed. | `DA:WITH_ISSUES` | Data available. Issues were detected in the data which probably need to be curated. @@ -1637,6 +1637,9 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.18.0:: +Changes in 1.17.1:: +* https://github.com/devonfw/solicitor/issues/223: Fixed a regression bug in Solicitor command line parsing which (within Version 1.16.0 and 1.17.0) prevents correct parsing of "l" and "d" options. + Changes in 1.17.0:: * https://github.com/devonfw/solicitor/issues/221: Provide status information on data obtained from Scancode in field `ApplicationComponent.dataStatus`. See <>. From 3e5db472764820c6502927ad15435a5bcd90d69c Mon Sep 17 00:00:00 2001 From: duph97 Date: Mon, 29 Jan 2024 13:29:13 +0100 Subject: [PATCH 091/139] Add new license name mapping rules (#225) * Add new license name mapping rules * Remove project specific name mapping rules * Fix Sorting. Sort by column E (Normalized SPDX-Id) * Updated release notes --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../rules/LicenseNameMappingSample.xls | Bin 89088 -> 198656 bytes documentation/master-solicitor.asciidoc | 1 + 2 files changed, 1 insertion(+) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls index 513946d0217eedb2f907f41d30bfbca10f7e72a9..97739828e8b2e2f6567fc9a0810010c66e665ff6 100644 GIT binary patch literal 198656 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ⅆ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 Date: Mon, 29 Jan 2024 22:14:45 +0100 Subject: [PATCH 092/139] Version 1.18.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index e71a3fc7..f4a036d5 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.18.0-SNAPSHOT + 1.18.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 0a310e75..3c3754bf 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.18.0-SNAPSHOT + 1.18.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 8cd027cf..54a45035 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.18.0-SNAPSHOT + 1.18.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index c81fafab..75d52dd0 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.18.0-SNAPSHOT + 1.18.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 0e22b7f8..2e4ca441 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.18.0-SNAPSHOT + 1.18.0 pom Solicitor Aggregator From 32de09c0e58cc34a60ccc7178906e292b17b2d85 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:21:51 +0100 Subject: [PATCH 093/139] Set version to SNAPSHOT of next minor release (1.19.0-SNAPSHOT) --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index f4a036d5..4ce359a0 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.18.0 + 1.19.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 3c3754bf..9dcc6799 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.18.0 + 1.19.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index fdaa592b..83076015 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1635,6 +1635,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.19.0:: + Changes in 1.18.0:: * https://github.com/devonfw/solicitor/pull/225: Added some additional name mapping rules to handle SPDX-IDs and license references from Scancode. diff --git a/documentation/pom.xml b/documentation/pom.xml index 54a45035..3c1eb096 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.18.0 + 1.19.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 75d52dd0..ee72fe4d 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.18.0 + 1.19.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 2e4ca441..ee09f049 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.18.0 + 1.19.0-SNAPSHOT pom Solicitor Aggregator From b41c48ac77f561f3187f7898753180ecb64ccc5a Mon Sep 17 00:00:00 2001 From: duph97 Date: Thu, 1 Feb 2024 12:14:28 +0100 Subject: [PATCH 094/139] Bugfix data status aggregated inventory (#226) * Add dataStatus to select statement * Add release note * removed comments * Reference (newly created) bug issue instead of pull request. --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../sql/normalizedlicenses_aggregated_applications.sql | 5 ++++- documentation/master-solicitor.asciidoc | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql index 010451a7..bd1baedb 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql @@ -18,8 +18,9 @@ select "packageUrl", "ossHomepage", "sourceRepoUrl", + "dataStatus", "usagePattern", - "ossModified", + "ossModified", "declaredLicense", "licenseUrl", "normalizedLicenseType", @@ -52,6 +53,7 @@ from ( ac."packageUrl", ac."ossHomepage", ac."sourceRepoUrl", + ac."dataStatus", ac."usagePattern", ac."ossModified", l."declaredLicense", @@ -88,6 +90,7 @@ from ( "packageUrl", "ossHomepage", "sourceRepoUrl", + "dataStatus", "usagePattern", "ossModified", "declaredLicense", diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 83076015..7744b4f1 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1636,6 +1636,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.19.0:: +* https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. Changes in 1.18.0:: * https://github.com/devonfw/solicitor/pull/225: Added some additional name mapping rules to handle SPDX-IDs and license references from Scancode. From 13d18b473af05400a4d9ca07acd53238247e53d8 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 1 Feb 2024 15:02:12 +0100 Subject: [PATCH 095/139] introduce dataStatus prefix NL if no licenses were obtained from scancode (#229) --- .../ComponentInfoInventoryProcessor.java | 11 ++++++++++- documentation/master-solicitor.asciidoc | 16 ++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index b631ab4f..7535e603 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -41,7 +41,8 @@ public void add(Statistics statistics) { } /** - * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that data is available. + * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that data is available. In this case the + * information as obtained from the Readers is preserved. */ private static final String DA_STATUS_PREFIX = "DA:"; @@ -50,6 +51,12 @@ public void add(Statistics statistics) { */ private static final String ND_STATUS_PREFIX = "ND:"; + /** + * Prefix used for {@link ApplicationComponent#getDataStatus()} in case data is available but does not contain any + * license information. In this case the license information (RawLicenses) as obtained from the Readers is prserved. + */ + private static final String NL_STATUS_PREFIX = "NL:"; + /** * Origin data for raw license objects created by this Class. Due to compatibility reasons this is named "scancode" * even due to the fact that it might originate from other sources. @@ -166,6 +173,8 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { } else { LOG.info(LogMessages.COMPONENTINFO_NO_LICENSES.msg(), (ac.getGroupId() != null ? ac.getGroupId() + "/" : "") + ac.getArtifactId() + "/" + ac.getVersion()); + // Correct dataStatus + ac.setDataStatus(NL_STATUS_PREFIX + componentInfo.getDataStatus()); for (RawLicense rl : ac.getRawLicenses()) { String trace = rl.getTrace() + System.lineSeparator() + "+ ComponentInfo available but without license information - keeping data from Reader"; diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 7744b4f1..8e304862 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1434,12 +1434,15 @@ When using the Scancode integration the following values are used for field `App [width="100%",cols="1,3",options="header"] |==================== | Value | Description -| `ND:DISABLED` | No data available. Scancode integration disabled. -| `ND:NOT_AVAILABLE` | No data available. No scan results existing and no indication that attempting download/scanning has failed. -| `ND:PROCESSING_FAILED` | No data available. No scan results existing. Processing (downloading or scanning) had failed. -| `DA:WITH_ISSUES` | Data available. Issues were detected in the data which probably need to be curated. -| `DA:NO_ISSUES` | Data available. No curations applied. No issues were detected. -| `DA:CURATED` | Data available. Curations were applied. No issues were detected. +| `ND:DISABLED` | No data available. Scancode integration disabled. License info from reader was preserved. +| `ND:NOT_AVAILABLE` | No data available. No scan results existing and no indication that attempting download/scanning has failed. License info from reader was preserved. +| `ND:PROCESSING_FAILED` | No data available. No scan results existing. Processing (downloading or scanning) had failed. License info from reader was preserved. +| `NL:WITH_ISSUES` | Data available but did not contain any license infos. Issues were detected in the data which probably need to be curated. License info from reader was preserved. +| `NL:NO_ISSUES` | Data available but did not contain any license infos. No curations applied. No issues were detected (despite the fact that no license info was found). License info from reader was preserved. +| `NL:CURATED` | Data available but did not contain any license infos. Curations were applied. No issues were detected (despite the fact that no license info was found). License info from reader was preserved. +| `DA:WITH_ISSUES` | Data available (including licenses). Issues were detected in the data which probably need to be curated. +| `DA:NO_ISSUES` | Data available (including licenses). No curations applied. No issues were detected. +| `DA:CURATED` | Data available (including licenses). Curations were applied. No issues were detected. |==================== === Correcting data @@ -1637,6 +1640,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.19.0:: * https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. +* https://github.com/devonfw/solicitor/issues/228: Extended `ApplicationComponent.dataStatus` values to reflect situation when no licenses were obtained from the Scancode information. See <>. Changes in 1.18.0:: * https://github.com/devonfw/solicitor/pull/225: Added some additional name mapping rules to handle SPDX-IDs and license references from Scancode. From 629f4474c4848878a49cb74966573631faf6d1dc Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 5 Feb 2024 09:19:13 +0100 Subject: [PATCH 096/139] Fixed Javadoc --- .../componentinfo/ComponentInfoInventoryProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 7535e603..c174b47d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -41,13 +41,13 @@ public void add(Statistics statistics) { } /** - * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that data is available. In this case the - * information as obtained from the Readers is preserved. + * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that data is available. */ private static final String DA_STATUS_PREFIX = "DA:"; /** - * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that no data is available. + * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that no data is available. In this case the + * information as obtained from the Readers is preserved. */ private static final String ND_STATUS_PREFIX = "ND:"; From c89434809935d0c6eea015328f05904c23cf2d8a Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:04:23 +0100 Subject: [PATCH 097/139] Automatically map scancode data to NormalizedLicenses (#231) * Core implementation of automatic mapping of scancode generated RawLicenses to NormalizedLicenses (https://github.com/devonfw/solicitor/issues/230) * completion of own license info (cherry picked from commit 94359b68a42a554846d6ba66a60cad4f942ef864) * Update documentation * add ignorelist * change logic for detecting if dataStatus is WITH_ISSUES to use regex list * Refactoring of regex list processing * some cleanup * fixed spelling issues --- core/pom.xml | 5 + .../tools/solicitor/common/LogMessages.java | 15 +- .../solicitor/common/RegexListPredicate.java | 102 +++++++ .../CuratingComponentInfoAdapter.java | 53 +++- ...ncodeLicenseMappingInventoryProcessor.java | 267 ++++++++++++++++++ .../src/main/resources/application.properties | 8 + .../rules/LicenseAssignmentV2Sample.xls | Bin 62976 -> 63488 bytes .../rules/MultiLicenseSelectionSample.xls | Bin 34304 -> 34304 bytes .../resources/spdx-java-library.properties | 2 + .../common/RegexListPredicateTest.java | 88 ++++++ ...eLicenseMappingInventoryProcessorTest.java | 238 ++++++++++++++++ ...content_com_jhy_jsoup_jsoup_1_15_3_LICENSE | 21 ++ documentation/files/application.properties | 8 + documentation/master-solicitor.asciidoc | 42 ++- solicitor.dict | 1 + 15 files changed, 832 insertions(+), 18 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/common/RegexListPredicate.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeLicenseMappingInventoryProcessor.java create mode 100644 core/src/main/resources/resources/spdx-java-library.properties create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/common/RegexListPredicateTest.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeLicenseMappingInventoryProcessorTest.java create mode 100644 core/src/test/resources/licenses/http___raw_githubusercontent_com_jhy_jsoup_jsoup_1_15_3_LICENSE diff --git a/core/pom.xml b/core/pom.xml index 9dcc6799..182ee23b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -163,6 +163,11 @@ java-jwt 4.4.0 + + org.spdx + java-spdx-library + 1.1.10 + ${project.groupId} solicitor-documentation diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 67d9aa15..023bc065 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -76,7 +76,6 @@ public enum LogMessages { MULTIPLE_DECISIONTABLES(51, "Multiple decision tables in both .xls and .csv format. Prioritizing '{}.xls'."), // ADDING_ADDITIONALWRITER_CONFIG(52, "Merging config: Adding additional writers to base config from {}"), // NOT_A_VALID_NPM_PACKAGE_IDENTIFIER(53, "{} is not a valid identifier for an NPM package"), // - NOT_A_VALID_NPM_PACKAGE_NAME(54, "{} is not a valid name for an NPM package"), // SCANCODE_PROCESSOR_STARTING(54, "Experimental feature ACTIVE: Start enriching the inventory data with Scancode data (as far as available)"), // SCANCODE_FEATURE_DEACTIVATED(55, @@ -88,7 +87,19 @@ public enum LogMessages { CLASSPATHEXCEPTION_WITHOUT_GPL(60, "ClassPathException was found but no GPL License exists for {}"), // CLASSPATHEXCEPTION_MULTIPLE_GPL(61, "ClassPathException was found but there are multiple GPL Licenses for {}"), // CYCLONEDX_UNSUPPORTED_PURL(62, - "The CycloneDX file contains the PackageURL '{}' with unsupported type which will be ignored. Solicitor reports might be incomplete."); + "The CycloneDX file contains the PackageURL '{}' with unsupported type which will be ignored. Solicitor reports might be incomplete."), // + SCANCODE_AUTOMAPPING_STARTED(63, + "Attempting to automatically map scancode license id to create NormalizedLicense objects. Blacklist: '{}', Ignorelist: '{}'"), // + SCANCODE_AUTOMAPPING_FEATURE_DEACTIVATED(64, + "The feature of attempting to automatically map scancode license ids is DEACTIVATED"), // + SCANCODE_NO_MAPPING(65, + "The license info '{}' from Scancode could not be mapped to OSS-SPDX or SCANCODE type license info"), // + SCANCODE_MAPPING_STATISTICS(66, + "Statistics for automatic mapping of scancode license ids to NormalizedLicenses: Total processed: {}, skipped due to blacklist: {}, " + + "skipped due to unkown SPDX: {}, mapped using type SCANCODE: {}, mapped using type OSS-SPDX: {}, mapped to IGNORE: {}"), // + NOT_A_VALID_NPM_PACKAGE_NAME(67, "{} is not a valid name for an NPM package"), // + SCANCODE_ISSUE_DETECTION_REGEX(68, + "The list of regular expressions for detecting licenses from scancode having issues is set to '{}'"); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/RegexListPredicate.java b/core/src/main/java/com/devonfw/tools/solicitor/common/RegexListPredicate.java new file mode 100644 index 00000000..21365e3f --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/RegexListPredicate.java @@ -0,0 +1,102 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ +package com.devonfw.tools.solicitor.common; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Encapsulates a list of regular expression {@link Pattern}s and allows checking if a string matches any of those + * patterns. + */ +public class RegexListPredicate implements Predicate { + + private static final Logger LOG = LoggerFactory.getLogger(RegexListPredicate.class); + + private Pattern[] patterns = new Pattern[0]; + + /** + * The constructor. + */ + public RegexListPredicate() { + + super(); + } + + /** + * Checks if the given argument matches any of the predefined regular expression patterns. + * + * @param t the string to be checked + * @param debugLogTemplate template to be used for creating a debug log message if the argument matches. This template + * should have two placeholders {} which will be filled with the value of the argument and the + * matching pattern. If this parameter is set to null then no debug logging will be done. + * @return true if the argument matches any of the patterns, false otherwise. + */ + public boolean test(String t, String debugLogTemplate) { + + if (t == null) { + return false; + } + for (Pattern p : this.patterns) { + if (p.matcher(t).matches()) { + if (LOG.isDebugEnabled() && debugLogTemplate != null) { + LOG.debug(debugLogTemplate, t, p.toString()); + } + return true; + } + } + return false; + } + + /** + * Tests the predicate without any debug logging. + * + * @param t the argument to be tested + * @return true if the argument matches any of the patterns, false otherwise. + * @see #test(String, String) + * @see Predicate#test(Object) + */ + @Override + public boolean test(String t) { + + return test(t, null); + } + + /** + * Sets the regular expressions to be tested by this object. + * + * @param regexes Array of strings, each being a valid regular expression expression as defined in {@link Pattern}. + * @throws PatternSyntaxException If any of the expressions have invalid syntax + */ + public void setRegexes(String[] regexes) { + + if (regexes != null) { + this.patterns = new Pattern[regexes.length]; + for (int i = 0; i < regexes.length; i++) { + this.patterns[i] = Pattern.compile(regexes[i]); + } + } + } + + /** + * Returns the list of configured regular expressions as a String + * + * @return The regexes as a single string, separated via comma. + */ + public String getRegexesAsString() { + + List patternStrings = new ArrayList(); + for (Pattern p : this.patterns) { + patternStrings.add(p.toString()); + } + return String.join(",", patternStrings); + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java index b71be000..4ce22feb 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java @@ -1,9 +1,13 @@ package com.devonfw.tools.solicitor.componentinfo.curation; -import java.util.Arrays; import java.util.Collection; -import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; + +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.RegexListPredicate; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; @@ -18,10 +22,16 @@ */ public class CuratingComponentInfoAdapter implements ComponentInfoAdapter { + private static final Logger LOG = LoggerFactory.getLogger(CuratingComponentInfoAdapter.class); + private FilteredComponentInfoProvider filteredComponentInfoProvider; private ComponentInfoCurator componentInfoCurator; + private RegexListPredicate licenseIdIssuesPredicate = new RegexListPredicate(); + + private boolean regexesLogged = false; + /** * The constructor. * @@ -86,16 +96,11 @@ private ComponentInfo checkForIssues(ComponentInfo componentInfo) { return componentInfo; } boolean issueExisting = false; - List possibleIssues = Arrays.asList("LicenseRef-scancode-free-unknown"); for (LicenseInfo li : licenses) { - for (String key : possibleIssues) { - if (key.equals(li.getSpdxid())) { - issueExisting = true; - break; - } - } - if (issueExisting) + if (isIssue(li.getSpdxid())) { + issueExisting = true; break; + } } if (issueExisting) { DefaultComponentInfoImpl result = new DefaultComponentInfoImpl(componentInfo); @@ -117,4 +122,32 @@ protected boolean isFeatureActive() { return true; } + /** + * Checks if the given license id falls in the category of "WITH_ISSUES". + * + * @param license the license id to check + * @return true if the license id matches the issue list. + */ + protected boolean isIssue(String license) { + + if (!this.regexesLogged) { + LOG.info(LogMessages.SCANCODE_ISSUE_DETECTION_REGEX.msg(), this.licenseIdIssuesPredicate.getRegexesAsString()); + this.regexesLogged = true; + } + return this.licenseIdIssuesPredicate.test(license, + "License id '{}' matches issue list via regex '{}' and result will be set to status WITH_ISSUES"); + } + + /** + * Sets the list of license ids which will be regarded as "WITH_ISSUES" + * + * @param licenseIdIssuesRegexes an array of regular expressions which define a the patterns of license ids which will + * be regarded as "WITH_ISSUES". + */ + @Value("${solicitor.scancode.issuelistpatterns}") + public void setLicenseIdIssuesRegexes(String[] licenseIdIssuesRegexes) { + + this.licenseIdIssuesPredicate.setRegexes(licenseIdIssuesRegexes); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeLicenseMappingInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeLicenseMappingInventoryProcessor.java new file mode 100644 index 00000000..68761f96 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeLicenseMappingInventoryProcessor.java @@ -0,0 +1,267 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spdx.library.model.license.LicenseInfoFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.InventoryProcessor; +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.RegexListPredicate; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.model.ModelFactory; +import com.devonfw.tools.solicitor.model.ModelRoot; +import com.devonfw.tools.solicitor.model.impl.ModelFactoryImpl; +import com.devonfw.tools.solicitor.model.impl.inventory.NormalizedLicenseImpl; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.inventory.NormalizedLicense; +import com.devonfw.tools.solicitor.model.inventory.RawLicense; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.ruleengine.drools.ModelHelper; + +/** + * An {@link InventoryProcessor} which maps {@link RawLicense}s originating from {@link ComponentInfo} scancode + * information to {@link NormalizedLicense} information. + */ +@Component +@Order(InventoryProcessor.BEFORE_RULE_ENGINE + 10) +public class ScancodeLicenseMappingInventoryProcessor implements InventoryProcessor { + + /** + * Data structure for aggregating statistics. + */ + public static class Statistics { + int total; + + int skippedBlacklist; + + int mappedLicenseRef; + + int mappedSpdx; + + int mappedIgnore; + + int skippedUnknown; + } + + /** + * Prefix used for {@link ApplicationComponent#getDataStatus()} in case that data is available. + */ + private static final String DA_STATUS_PREFIX = "DA:"; + + /** + * Prefix of licenses detected by scancode which do not correspond to a SPDX-ID. + */ + private static final String LICENSEREF_PREFIX = "LicenseRef-scancode"; + + /** + * Origin data for raw license objects originating from scancode data. + */ + private static final String ORIGIN_COMPONENTINFO = "scancode"; + + private static final Logger LOG = LoggerFactory.getLogger(ScancodeLicenseMappingInventoryProcessor.class); + + private ModelFactory modelFactory; + + private RegexListPredicate mappingBlacklistPredicate = new RegexListPredicate(); + + private RegexListPredicate mappingIgnorelistPredicate = new RegexListPredicate(); + + private boolean featureFlag; + + /** + * The constructor. + */ + public ScancodeLicenseMappingInventoryProcessor() { + + } + + @Override + public void processInventory(ModelRoot modelRoot) { + + if (isFeatureActive()) { + Statistics stat = new Statistics(); + for (Application application : modelRoot.getEngagement().getApplications()) { + for (ApplicationComponent ac : application.getApplicationComponents()) { + processApplicationComponent(ac, stat); + } + } + LOG.info(LogMessages.SCANCODE_MAPPING_STATISTICS.msg(), stat.total, stat.skippedBlacklist, stat.skippedUnknown, + stat.mappedLicenseRef, stat.mappedSpdx, stat.mappedIgnore); + } + } + + /** + * Processes a single {@link ApplicationComponent} and tries to automatically create a {@link NormalizedLicense} + * object for all {@link RawLicense} objects which originate from scancode information. + * + * @param ac The {@link ApplicationComponent} to be processed. + * @param stat An object which holds the aggregated processing statistics + */ + protected void processApplicationComponent(ApplicationComponent ac, Statistics stat) { + + if (ac.getDataStatus() != null && ac.getDataStatus().startsWith(DA_STATUS_PREFIX)) { + for (RawLicense rl : ac.getRawLicenses()) { + if (ORIGIN_COMPONENTINFO.equals(rl.getOrigin()) && rl.getDeclaredLicense() != null) { + stat.total++; + String license = rl.getDeclaredLicense(); + if (isBlacklisted(license)) { + stat.skippedBlacklist++; + continue; + } + if (isToBeIgnored(license)) { + stat.mappedIgnore++; + NormalizedLicenseImpl nl = ((ModelFactoryImpl) this.modelFactory).newNormalizedLicense(rl); + rl.setSpecialHandling(true); + nl.setNormalizedLicense("Ignore"); + nl.setNormalizedLicenseType("IGNORE"); + nl.setNormalizedLicenseUrl(rl.getLicenseUrl()); + ModelHelper.appendTraceToNormalizedLicense(nl, "+ mapped detected Scancode license ref (" + license + + ") to IGNORE/Ignore as it matches the given pattern list"); + } else if (license.startsWith(LICENSEREF_PREFIX)) { + stat.mappedLicenseRef++; + NormalizedLicenseImpl nl = ((ModelFactoryImpl) this.modelFactory).newNormalizedLicense(rl); + rl.setSpecialHandling(true); + nl.setNormalizedLicense(license); + nl.setNormalizedLicenseType("SCANCODE"); + nl.setNormalizedLicenseUrl(rl.getLicenseUrl()); + ModelHelper.appendTraceToNormalizedLicense(nl, + "+ mapped detected Scancode license ref (" + license + ") to SCANCODE/" + license); + } else if (isSpdxLicensePossiblyWithException(license)) { + stat.mappedSpdx++; + NormalizedLicenseImpl nl = ((ModelFactoryImpl) this.modelFactory).newNormalizedLicense(rl); + rl.setSpecialHandling(true); + nl.setNormalizedLicense(license); + nl.setNormalizedLicenseType("OSS-SPDX"); + nl.setNormalizedLicenseUrl(rl.getLicenseUrl()); + ModelHelper.appendTraceToNormalizedLicense(nl, + "+ mapped license detected by Scancode (" + license + ") to OSS-SPDX/" + license); + } else { + stat.skippedUnknown++; + LOG.warn(LogMessages.SCANCODE_NO_MAPPING.msg(), license); + } + } + } + } + + } + + /** + * Checks if the given license id is blacklisted and should not be mapped automatically to a + * {@link NormalizedLicense}. + * + * @param license the license id to check + * @return true if the license id is blacklisted. + */ + protected boolean isBlacklisted(String license) { + + return this.mappingBlacklistPredicate.test(license, + "License id '{}' is blacklisted via regex '{}' and will not be mapped to a NormalizedLicense"); + } + + /** + * Checks if the given license id shall be mapped to a {@link NormalizedLicense} with pseudo license id Ignore. + * + * @param license the license id to check + * @return true if the license id matches the ignore list. + */ + protected boolean isToBeIgnored(String license) { + + return this.mappingIgnorelistPredicate.test(license, "License id '{}' matches ignore list via regex '{}' and " + + "will be mapped to a NormalizedLicense using pseudo license Ignore"); + } + + /** + * Checks if the argument represents a SPDX-ID for a license or a license and an Exception, both given by their + * SPDX-IDs + * + * @param license the license string to check + * @return true if this is a license (with optional Exception) + */ + protected boolean isSpdxLicensePossiblyWithException(String license) { + + if (license == null) { + return false; + } + if (license.contains("WITH")) { + String[] parts = license.split("WITH"); + if (parts.length != 2) { + return false; + } + String pureLicense = parts[0].trim(); + String exception = parts[1].trim(); + return LicenseInfoFactory.isSpdxListedLicenseId(pureLicense) + && LicenseInfoFactory.isSpdxListedExceptionId(exception); + } else { + return LicenseInfoFactory.isSpdxListedLicenseId(license); + } + + } + + /** + * This method sets the field modelFactory. + * + * @param modelFactory the new value of the field modelFactory + */ + @Autowired + public void setModelFactory(ModelFactory modelFactory) { + + this.modelFactory = modelFactory; + } + + /** + * Sets the blacklist of license ids which will not be attempted to be mapped automatically. + * + * @param licenseIdMappingBlacklistRegexes an array of regular expressions which define a blacklist of license ids + * which will not be mapped automatically to {@link NormalizedLicense} information. + */ + @Value("${solicitor.scancode.automapping.blacklistpatterns}") + public void setLicenseIdMappingBlacklistRegexes(String[] licenseIdMappingBlacklistRegexes) { + + this.mappingBlacklistPredicate.setRegexes(licenseIdMappingBlacklistRegexes); + } + + /** + * Sets the list of license ids which will be mapped to {@link NormalizedLicense} using IGNORE/Ignore. + * + * @param licenseIdMappingIgnorelistRegexes an array of regular expressions which define a list of license ids which + * will be mapped to @link NormalizedLicense} information using type/pseudolicense IGNORE/Ignore. + */ + @Value("${solicitor.scancode.automapping.ignorelistpatterns}") + public void setLicenseIdMappingIgnorelistRegexes(String[] licenseIdMappingIgnorelistRegexes) { + + this.mappingIgnorelistPredicate.setRegexes(licenseIdMappingIgnorelistRegexes); + + } + + /** + * Sets the feature flag for activating/deactivating this feature. + * + * @param featureFlag the flag + */ + @Value("${solicitor.feature-flag.scancode.automapping}") + public void setFeatureFlag(boolean featureFlag) { + + this.featureFlag = featureFlag; + } + + /** + * Check if the feature flag is set and log the result. + * + * @return the value of the feature flag + */ + protected boolean isFeatureActive() { + + if (this.featureFlag) { + LOG.info(LogMessages.SCANCODE_AUTOMAPPING_STARTED.msg(), this.mappingBlacklistPredicate.getRegexesAsString(), + this.mappingIgnorelistPredicate.getRegexesAsString()); + } else { + LOG.info(LogMessages.SCANCODE_AUTOMAPPING_FEATURE_DEACTIVATED.msg()); + } + return this.featureFlag; + } + +} diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index b81ecb0d..b3496110 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -46,6 +46,12 @@ solicitor.deprecated-features-allowed=false ## Feature flags for activation of non-standard/experimental functionality # Incorporate scancode infos into model solicitor.feature-flag.scancode=false +# Attempt automatic mapping of scancode license ids to NormalizedLicenses +solicitor.feature-flag.scancode.automapping=true +# comma separated list of regular expressions which define the scancode license ids which will not be automatically mapped +solicitor.scancode.automapping.blacklistpatterns=.*unknown.*,.*proprietary.* +# comma separated list of regular expressions which define the scancode license ids which will be mapped to IGNORE/Ignore +solicitor.scancode.automapping.ignorelistpatterns= ## Parameters for controlling the processing of scancode information # minimum score of detected license findings to be taken into account for Solicitor processing @@ -56,6 +62,8 @@ solicitor.scancode.min-licensefile-number-of-lines=5 solicitor.scancode.curations-filename=output/curations.yaml # base path of the file repo where sources and scancode information is stored solicitor.scancode.repo-base-path=output/Source +# list of patterns of found licenses which shall set the dataStatus to WITH_ISSUES +solicitor.scancode.issuelistpatterns=LicenseRef-scancode-free-unknown # The curationDataSelector value to use when accessing curation data. # You can change its value to select a specific curation data source (if the implementation supports this). diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseAssignmentV2Sample.xls index 5cfbbc75b4976924513d3fe0688ae7ec8ff7de06..3bbf16526cdca12516ab49ba6f84b928ec821a33 100644 GIT binary patch delta 3362 zcmZwJdu&rx7y$6^+;;1`dzZmj*Wt92u@~;46tK7#1DwOn!O#hH>t1Z@Y-ggN+$Ok0 z5<-jzV~8d*CjNo=f}5yI2u3HtDB|=V4UPm8-5+PwvwP3?JLlZ< z>{+9K8=`ZDPmO{l0s!}xmzT#RhwwSGhqX{9K5fWmNBHlCyf=(Prs+FB{ZZR_okxu0 zg5id=X#Cn>iSka_n1Qo^?}BB9v9tV!)z94AZOc!(=XD9-PGLJB3QU4Kw<>6MW8A$mDyjH9Ph~Idt(|cAJvn;zKK`Q%cJ3$hEFj3NryF z_(glZQZSp}vOBa?xNXtIHnp^II)N`v?19Dpy>+m#TT?dz$MSTG|?F0ontHgPx&*?%+ry=ot&X zIOgdMhlV})%5Zlu;tBP7#`=Sv?oc=!jEsgxdIm=NJY#`AX|{FI>5_Lz$5DWq)7Ym= z098`}H!t&l4z{udsp-)1BIf1azgi&vn$e7_3h)Zp!GM>~-n0@MAq7sXiN~ps3SM1o z0u!81i|3nhBb+aZ#}=@F*B_6qU2)`O)!$46mgLJB1&?sXW5` z+_+a0A?Xw>1G-vngL6!HX~uUCQ0RReUi+iH<7g}4V(3-bp{tasj`(Y8)0_B z9E6GTT&x~l8e!>Z3#(=(gRo4(vIxsoO~eO4Z2EIlOfVN=YY59FY;A(=RAzczf_15w zmw@VCSWj3!VQ#_-RFg_xo61?W#R`ey8wk6Pup+|lC+q>eiA)`%SC85jD<vSYaq=YrWl8HlyUC>4$*U>S?a3L&PkrT-E_^w| zZ1Qx~Vc8kxljbLXW76p5d(3c`ZR7J&mUMDzmhqRbxP^+1{GBVs;z?Ly>B3(uel4*j z=2Z5iA6qtPCs%Cpb`bU$VReMnC!2^9k=#flVNHbXB&?aRU4-pcOgmy?quWCie4MZr z!k!?km9V{f6Sc4ZlX}#wjj(pYo+7M+uziGy`w8qM>;Pd;6ZQ;YU4%WWn*7fUKiY^7 zswlO6mnW=RnE>&Q=LqX2tcS25VZC~btxTUDwSM*!HbB@QVTTDDB5dC1*YA@*#-Jf`=idEN8Hg*YX7$W%w(R aU;8;H8^`^T9Js2lItK`ap^ZYFmfR8l8Uf2a) z&;zwF4<`6?{4hxP1&tCO9pRC}XcbJm@6FJ=a+Db7@?e>5XiRmwL6Jp3r#-WyV`3Cn z%Yzj{OeDzA5v5WH%jn;VYK=-&bEGQ-R@td!Xk3j}XL5@%6I$auJFVIYJ>@_np&P1^ z&@B=6Yb(T)%4vrd;G?*z)7=`6vjf@(%caYkXm<69;?0q6-lEUh$;M=arIT4r@8iXf z8~`{?0X`Z9Sa1d4vvE4OwUcDo&8>$QlNjwjQk}XQT5t3Y4uS$6ek7Fk*|`^daCELS zFM=Y7x#q=C43|orxSub^rDe`M00D@_op}&~kkFmE4DuW}8g_uflZeqse|0DpXy-}s zkEDr9A5HW>8p(=rnk2w4m5MY=AWIMRhnsREUasSj9JLQ&MJA(HT@IbX+mEON2n$*c z(GT-;P!P3>Frz-BioODeQXcs?uZj@D=9r9rQ{YgBs3Qo|Er-xReih}2x*~_I6aAWJ zIdl~`EXYxtD_4oID$C*gK$yRw-wk{lsmWcyD00&x`=&Gbi7!Ufk05M`9hM^M z8iYM&IV5KtfRu=)EO)7Gw!+2I#B5&7ayT~}PT4h^k(G$L*2QesIv2BD>jm=~TPN78 z0nx2;GtElr2@}%hvrb#gl%90UE%j=IHChfs1rALvwOzm(ggtFCcJu6~Vc!`1L+aSv zuOE;~qp()^k8dXRK%fma^7$CtBlUboXrt#p4zreve-YWJwUq~^NdqIJe-gGR^OKJx zSZ!vC&>t>q%scuU5x$LVc>DojXU4Nc4nh+*UCR%N4oyU*3pMnsiKX<92|cBWMUC^Y z$NE*;**7Ds1!1iSYeQH9VQW(eY)9BSgsn%|283-y*fSPO@qU6`@w14q17Xi0tP^2L zggu|jQlkAY}BY(gRZ4{3t?{~Y#YM55w_jUQr6bC!woIA6Jfg$_71|{MOY8QT6+<=8)5GuY>&z4 z)Gzmg*I)^>@IO9rLgPO#q{`&u;RcY67aSV+A^DKS4&)#kxWFVn zMP+t&MrW|kGZ~V=p^?TnOPQd_cQ4Z{v&7W?VQ~&}svF z4Vw=z7|b`Huu5kW;ALR&aS!m(HPSQG&CkoJ+^p}|#>e_Hoq<7g@-F{C#-E#c0$wXR z`pLKhLz#i0mVpOIGck0EBlye=%na&Cd=>@{h9qf(JSW3H2KC7nMeg-KfhGU}BR>NN zlK|Mf{|u;N!02Z{7h}O9#)?IZ4T~5%vRFOTVh&_Ms2C>}F)l1(+*rhb;f>*FV7Ow4 zIrCu=P*wUKzz3j1Eu!w?8(!F}awfl`(4bPnILh9GiE7L>QSTKMb(m z#CeC2arxxCTn>y3lRt1vGczz?qh)OwSQ#A|iWo8(5*czB3>l;tPB1Vs{IB1`1k?b; z_+^16i*Ru;Okjj426APA*75PkG8#=T;1T0tVBmyF!URN+1r)*RB)B*kp!R`PO+LlL z4V0+im6-gFhZD&90X0x$vKB8dSc}MHA1F(L%M78ph8JYwJYIzTCdk%-9ApC*n8c^3 zY{bs!4EA{@LlQU?3>mZ-GJxJOgDact!lw(i6Qr99Av}c-XxHX#d{Nqr6DJE=ZD7w~ z^8p5f+2#{g>1>9qSaPletK*s-@WddF+I(o{ug94SImVpOIGcimONAQ^$ zm>E=%_$&+@47t(>c}|9Z3@Vd7irnk}15E$|Mt%kkCIPT{{~1ulK(UQ3#)3tR6^j@f z7BO~Yv3jV*9LR!DF-|OETv)`ov4{bK7sJuKSj3$9un6*F5fcKXWVDbGhKOyxP;Ab^ z?9L#ynX4j=nNI+y8yKJr0zk;Xu(_`K5)06G8R>$&G50q|G^}8pY``M0sEGvtT_K`! diff --git a/core/src/main/resources/resources/spdx-java-library.properties b/core/src/main/resources/resources/spdx-java-library.properties new file mode 100644 index 00000000..a9a54028 --- /dev/null +++ b/core/src/main/resources/resources/spdx-java-library.properties @@ -0,0 +1,2 @@ +# only use builtin SPDX-ID information (not accessing web service) +org.spdx.useJARLicenseInfoOnly=true diff --git a/core/src/test/java/com/devonfw/tools/solicitor/common/RegexListPredicateTest.java b/core/src/test/java/com/devonfw/tools/solicitor/common/RegexListPredicateTest.java new file mode 100644 index 00000000..34bec511 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/common/RegexListPredicateTest.java @@ -0,0 +1,88 @@ +package com.devonfw.tools.solicitor.common; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.regex.PatternSyntaxException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link RegexListPredicate}. + * + */ +class RegexListPredicateTest { + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.common.RegexListPredicate#test(java.lang.String, java.lang.String)}. + */ + @Test + void testTestStringString() { + + RegexListPredicate objectUnderTest = new RegexListPredicate(); + assertFalse(objectUnderTest.test("", "Match! String: {}, Pattern: {}")); + assertFalse(objectUnderTest.test(null, "Match! String: {}, Pattern: {}")); + + objectUnderTest.setRegexes(new String[] { ".*aa.*", "b.*" }); + + assertFalse(objectUnderTest.test("", "Match! String: {}, Pattern: {}")); + assertFalse(objectUnderTest.test(null, "Match! String: {}, Pattern: {}")); + assertFalse(objectUnderTest.test("somelicense", "Match! String: {}, Pattern: {}")); + assertFalse(objectUnderTest.test("abkkkkk", "Match! String: {}, Pattern: {}")); + assertTrue(objectUnderTest.test("bkkkkk", "Match! String: {}, Pattern: {}")); + assertTrue(objectUnderTest.test("aa", "Match! String: {}, Pattern: {}")); + + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.common.RegexListPredicate#test(java.lang.String)}. + */ + @Test + void testTestString() { + + RegexListPredicate objectUnderTest = new RegexListPredicate(); + assertFalse(objectUnderTest.test("")); + assertFalse(objectUnderTest.test(null)); + + objectUnderTest.setRegexes(new String[] { ".*aa.*", "b.*" }); + + assertFalse(objectUnderTest.test("")); + assertFalse(objectUnderTest.test(null)); + assertFalse(objectUnderTest.test("somelicense")); + assertFalse(objectUnderTest.test("abkkkkk")); + assertTrue(objectUnderTest.test("bkkkkk")); + assertTrue(objectUnderTest.test("aa")); + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.common.RegexListPredicate#setRegexes(java.lang.String[])}. + */ + @Test + void testSetRegexes() { + + RegexListPredicate objectUnderTest = new RegexListPredicate(); + objectUnderTest.setRegexes(null); + objectUnderTest.setRegexes(new String[] { ".*aa.*", "b.*" }); + Assertions.assertThrows(PatternSyntaxException.class, () -> { + objectUnderTest.setRegexes(new String[] { "[ab" }); + }); + + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.common.RegexListPredicate#getRegexesAsString()}. + */ + @Test + void testGetRegexesAsString() { + + RegexListPredicate objectUnderTest = new RegexListPredicate(); + assertEquals("", objectUnderTest.getRegexesAsString()); + + objectUnderTest.setRegexes(new String[] { ".*aa.*", "b.*" }); + assertEquals(".*aa.*,b.*", objectUnderTest.getRegexesAsString()); + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeLicenseMappingInventoryProcessorTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeLicenseMappingInventoryProcessorTest.java new file mode 100644 index 00000000..629e065d --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeLicenseMappingInventoryProcessorTest.java @@ -0,0 +1,238 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collections; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics; +import com.devonfw.tools.solicitor.model.impl.ModelFactoryImpl; +import com.devonfw.tools.solicitor.model.impl.inventory.NormalizedLicenseImpl; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.inventory.RawLicense; + +/** + * Test for class {@link ScancodeLicenseMappingInventoryProcessor}. + */ +class ScancodeLicenseMappingInventoryProcessorTest { + + private ModelFactoryImpl mf; + + private NormalizedLicenseImpl nl; + + private ApplicationComponent ac; + + private ScancodeLicenseMappingInventoryProcessor objectUnderTest; + + private Statistics stat; + + void initObjects(String origin, String dataStatus, String license) { + + // provide required ModelFactory + this.mf = Mockito.mock(ModelFactoryImpl.class); + this.nl = new NormalizedLicenseImpl(); + Mockito.when(this.mf.newNormalizedLicense(Mockito.any())).thenReturn(this.nl); + this.objectUnderTest = new ScancodeLicenseMappingInventoryProcessor(); + this.objectUnderTest.setModelFactory(this.mf); + this.objectUnderTest.setLicenseIdMappingBlacklistRegexes(new String[] { ".*blacklisted.*" }); + this.objectUnderTest.setLicenseIdMappingIgnorelistRegexes(new String[] { ".*ignore.*" }); + + this.stat = new Statistics(); + + // List rll = new ArrayList<>(); + + // a SPDX raw license + RawLicense rl = Mockito.mock(RawLicense.class); + Mockito.when(rl.getOrigin()).thenReturn(origin); + Mockito.when(rl.getDeclaredLicense()).thenReturn(license); + + // the containing ApplicationComponent + this.ac = Mockito.mock(ApplicationComponent.class); + Mockito.when(this.ac.getDataStatus()).thenReturn(dataStatus); + Mockito.when(this.ac.getRawLicenses()).thenReturn(Collections.singletonList(rl)); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#processApplicationComponent(com.devonfw.tools.solicitor.model.inventory.ApplicationComponent, com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics)}. + */ + @Test + void testProcessApplicationComponentOriginNotScancode() { + + initObjects("someother", "DA:FOOBAR", "Apache-2.0"); + + this.objectUnderTest.processApplicationComponent(this.ac, this.stat); + + Mockito.verify(this.mf, Mockito.times(0)).newNormalizedLicense(Mockito.any()); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#processApplicationComponent(com.devonfw.tools.solicitor.model.inventory.ApplicationComponent, com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics)}. + */ + @Test + void testProcessApplicationComponentDataStatusNull() { + + initObjects("scancode", null, "Apache-2.0"); + + this.objectUnderTest.processApplicationComponent(this.ac, this.stat); + + Mockito.verify(this.mf, Mockito.times(0)).newNormalizedLicense(Mockito.any()); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#processApplicationComponent(com.devonfw.tools.solicitor.model.inventory.ApplicationComponent, com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics)}. + */ + @Test + void testProcessApplicationComponentDataStatusNotDA() { + + initObjects("scancode", "NL:FOOBAR", "Apache-2.0"); + + this.objectUnderTest.processApplicationComponent(this.ac, this.stat); + + Mockito.verify(this.mf, Mockito.times(0)).newNormalizedLicense(Mockito.any()); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#processApplicationComponent(com.devonfw.tools.solicitor.model.inventory.ApplicationComponent, com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics)}. + */ + @Test + void testProcessApplicationComponentBlacklisted() { + + initObjects("scancode", "DA:FOOBAR", "LicenseRef-scancode-blacklisted-license"); + + this.objectUnderTest.processApplicationComponent(this.ac, this.stat); + + Mockito.verify(this.mf, Mockito.times(0)).newNormalizedLicense(Mockito.any()); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#processApplicationComponent(com.devonfw.tools.solicitor.model.inventory.ApplicationComponent, com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics)}. + */ + @Test + void testProcessApplicationComponentLicenseRef() { + + initObjects("scancode", "DA:FOOBAR", "LicenseRef-scancode-something"); + + this.objectUnderTest.processApplicationComponent(this.ac, this.stat); + + Mockito.verify(this.mf, Mockito.times(1)).newNormalizedLicense(Mockito.any()); + assertEquals("LicenseRef-scancode-something", this.nl.getNormalizedLicense()); + assertEquals("SCANCODE", this.nl.getNormalizedLicenseType()); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#processApplicationComponent(com.devonfw.tools.solicitor.model.inventory.ApplicationComponent, com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics)}. + */ + @Test + void testProcessApplicationComponentSPDX() { + + initObjects("scancode", "DA:FOOBAR", "Apache-2.0"); + + this.objectUnderTest.processApplicationComponent(this.ac, this.stat); + + Mockito.verify(this.mf, Mockito.times(1)).newNormalizedLicense(Mockito.any()); + assertEquals("Apache-2.0", this.nl.getNormalizedLicense()); + assertEquals("OSS-SPDX", this.nl.getNormalizedLicenseType()); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#processApplicationComponent(com.devonfw.tools.solicitor.model.inventory.ApplicationComponent, com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics)}. + */ + @Test + void testProcessApplicationComponentIgnore() { + + initObjects("scancode", "DA:FOOBAR", "LicenseRef-scancode-ignore-license"); + + this.objectUnderTest.processApplicationComponent(this.ac, this.stat); + + Mockito.verify(this.mf, Mockito.times(1)).newNormalizedLicense(Mockito.any()); + assertEquals("Ignore", this.nl.getNormalizedLicense()); + assertEquals("IGNORE", this.nl.getNormalizedLicenseType()); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#processApplicationComponent(com.devonfw.tools.solicitor.model.inventory.ApplicationComponent, com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor.Statistics)}. + */ + @Test + void testProcessApplicationComponentUnknown() { + + initObjects("scancode", "DA:FOOBAR", "Apache-0.9"); + + this.objectUnderTest.processApplicationComponent(this.ac, this.stat); + + Mockito.verify(this.mf, Mockito.times(0)).newNormalizedLicense(Mockito.any()); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#isSpdxLicensePossiblyWithException(java.lang.String)}. + */ + @Test + void testIsSpdxLicensePossiblyWithException() { + + this.objectUnderTest = new ScancodeLicenseMappingInventoryProcessor(); + assertTrue(this.objectUnderTest.isSpdxLicensePossiblyWithException("Apache-2.0")); + assertFalse(this.objectUnderTest.isSpdxLicensePossiblyWithException("Apache-1.2")); + assertFalse(this.objectUnderTest.isSpdxLicensePossiblyWithException("Classpath-exception-2.0")); + assertFalse(this.objectUnderTest.isSpdxLicensePossiblyWithException("WITH Classpath-exception-2.0")); + assertTrue(this.objectUnderTest.isSpdxLicensePossiblyWithException("GPL-2.0-only WITH Classpath-exception-2.0")); + assertFalse(this.objectUnderTest.isSpdxLicensePossiblyWithException("GPL-0.9-only WITH Classpath-exception-2.0")); + assertFalse(this.objectUnderTest.isSpdxLicensePossiblyWithException("GPL-2.0-only WITH Classpath-exception-1.0")); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#isBlacklisted(java.lang.String)}. + */ + @Test + void testIsBlacklisted() { + + this.objectUnderTest = new ScancodeLicenseMappingInventoryProcessor(); + this.objectUnderTest.setLicenseIdMappingBlacklistRegexes(new String[] { ".*aa.*", "b.*" }); + + assertFalse(this.objectUnderTest.isBlacklisted("")); + assertFalse(this.objectUnderTest.isBlacklisted("somelicense")); + assertFalse(this.objectUnderTest.isBlacklisted("abkkkkk")); + assertTrue(this.objectUnderTest.isBlacklisted("bkkkkk")); + assertTrue(this.objectUnderTest.isBlacklisted("aa")); + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeLicenseMappingInventoryProcessor#isToBeIgnored(java.lang.String)}. + */ + @Test + void testIsToBeIgnored() { + + this.objectUnderTest = new ScancodeLicenseMappingInventoryProcessor(); + this.objectUnderTest.setLicenseIdMappingIgnorelistRegexes(new String[] { ".*cc.*", "d.*" }); + + assertFalse(this.objectUnderTest.isToBeIgnored("")); + assertFalse(this.objectUnderTest.isToBeIgnored("somelicense")); + assertFalse(this.objectUnderTest.isToBeIgnored("cdkkkkk")); + assertTrue(this.objectUnderTest.isToBeIgnored("dkkkkk")); + assertTrue(this.objectUnderTest.isToBeIgnored("cc")); + } + +} diff --git a/core/src/test/resources/licenses/http___raw_githubusercontent_com_jhy_jsoup_jsoup_1_15_3_LICENSE b/core/src/test/resources/licenses/http___raw_githubusercontent_com_jhy_jsoup_jsoup_1_15_3_LICENSE new file mode 100644 index 00000000..fef8197f --- /dev/null +++ b/core/src/test/resources/licenses/http___raw_githubusercontent_com_jhy_jsoup_jsoup_1_15_3_LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009-2022 Jonathan Hedley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/documentation/files/application.properties b/documentation/files/application.properties index b81ecb0d..b3496110 100644 --- a/documentation/files/application.properties +++ b/documentation/files/application.properties @@ -46,6 +46,12 @@ solicitor.deprecated-features-allowed=false ## Feature flags for activation of non-standard/experimental functionality # Incorporate scancode infos into model solicitor.feature-flag.scancode=false +# Attempt automatic mapping of scancode license ids to NormalizedLicenses +solicitor.feature-flag.scancode.automapping=true +# comma separated list of regular expressions which define the scancode license ids which will not be automatically mapped +solicitor.scancode.automapping.blacklistpatterns=.*unknown.*,.*proprietary.* +# comma separated list of regular expressions which define the scancode license ids which will be mapped to IGNORE/Ignore +solicitor.scancode.automapping.ignorelistpatterns= ## Parameters for controlling the processing of scancode information # minimum score of detected license findings to be taken into account for Solicitor processing @@ -56,6 +62,8 @@ solicitor.scancode.min-licensefile-number-of-lines=5 solicitor.scancode.curations-filename=output/curations.yaml # base path of the file repo where sources and scancode information is stored solicitor.scancode.repo-base-path=output/Source +# list of patterns of found licenses which shall set the dataStatus to WITH_ISSUES +solicitor.scancode.issuelistpatterns=LicenseRef-scancode-free-unknown # The curationDataSelector value to use when accessing curation data. # You can change its value to select a specific curation data source (if the implementation supports this). diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 8e304862..9199eb23 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -213,16 +213,17 @@ Defines the type of license * `OSS-SPDX` - An OSS license which has a corresponding SPDX-Id * `OSS-OTHER` - An OSS license which has no SPDX-Id +* `SCANCODE` - A reference to a license represented by a `LicenseRef`-Id originating from Scancode. * `COMMERCIAL` - Commercial (non OSS) license; this might also include code which is owned by the project * `UNKNOWN`- License is unknown -* `IGNORED`- license will be ignored (non selected license in multi licensing case; only to be used as "Effective Normalized License Type") +* `IGNORED`- License will be ignored. If set on `normalizedLicenseType` (and `effectiveNormalizedLicenseType`) this indicates that the underlying RawLicense does not represent license information which is relevant in the given analysis. (E.g. a Contributor License Agreement might be qualified to be out of scope). If only set on `effectiveNormalizedLicenseType` this indicates that the license does not apply here - specifically due to selecting an alternative license in a multilicensing situation. ===== Pseudo License Ids -A "normalized" license id might be either a SPDX-Id or a "pseudo license id" which is used to indicate a specific situation. The following pseudo license ids are used: +A "normalized" license id might be either a SPDX-Id, a `LicenseRef`-Id or a "pseudo license id" which is used to indicate a specific situation. The following pseudo license ids are used: * `OSS specific` - a nonstandard OSS license which could not be mapped to a SPDX-Id * `PublicDomain` - any form of public domain which is not represented by an explicit SPDX-Id -* `Ignored` - license will be ignored (non selected license in multi licensing case; only to be used as "Effective Normalized License") +* `Ignored` - license will be ignored (see above) * `NonOSS` - commercial license, not OSS == Usage @@ -1437,14 +1438,41 @@ When using the Scancode integration the following values are used for field `App | `ND:DISABLED` | No data available. Scancode integration disabled. License info from reader was preserved. | `ND:NOT_AVAILABLE` | No data available. No scan results existing and no indication that attempting download/scanning has failed. License info from reader was preserved. | `ND:PROCESSING_FAILED` | No data available. No scan results existing. Processing (downloading or scanning) had failed. License info from reader was preserved. -| `NL:WITH_ISSUES` | Data available but did not contain any license infos. Issues were detected in the data which probably need to be curated. License info from reader was preserved. -| `NL:NO_ISSUES` | Data available but did not contain any license infos. No curations applied. No issues were detected (despite the fact that no license info was found). License info from reader was preserved. -| `NL:CURATED` | Data available but did not contain any license infos. Curations were applied. No issues were detected (despite the fact that no license info was found). License info from reader was preserved. +| `NL:WITH_ISSUES` | Data available but did not contain any license information. Issues were detected in the data which probably need to be curated. License info from reader was preserved. +| `NL:NO_ISSUES` | Data available but did not contain any license information. No curations applied. No issues were detected (despite the fact that no license info was found). License info from reader was preserved. +| `NL:CURATED` | Data available but did not contain any license information. Curations were applied. No issues were detected (despite the fact that no license info was found). License info from reader was preserved. | `DA:WITH_ISSUES` | Data available (including licenses). Issues were detected in the data which probably need to be curated. | `DA:NO_ISSUES` | Data available (including licenses). No curations applied. No issues were detected. | `DA:CURATED` | Data available (including licenses). Curations were applied. No issues were detected. |==================== +=== Automatic mapping of `RawLicense` data obtained from Scancode to `NormalizedLicense` + +Within the normal workflow `NormalizedLicense` objects are created from `RawLicense` objects via the rules given in the different LicenseAssignment and LicenseNameMapping decision tables, see <>. The "raw" license data obtained from Scancode represents licenses either by SPDX-IDs or (if licenses +are detected which do not have a corresponding SPDX-IDs) via `LicenseRef-scancode-XXXXX` qualifiers. This is an improved data quality as compared to `RawLicenses` obtained from normal Readers. (See <>.) _Solicitor_ makes use of this improved data quality and by default performs an automatic mapping of `RawLicense` data to `NormalizedLicense` s in this case: + +* If the raw license matches a SPDX-ID then a `NormalizedLicense` is created with `normalizedLicenseType` set to `OSS-SPDX`. +* If the raw license starts with `LicenseRef-scancode-` then a `NormalizedLicense` is created with `normalizedLicenseType` set to `SCANCODE`. +* If the raw license matches a given "ignorelist" (see below), then a `NormalizedLicense` is created with `normalizedLicenseType` set to `IGNORE` and `normalizeLicense` set to `Ignore`. +* If the raw license does not match any of the above criteria or matches a "blacklist" (see below) then no automatic mapping is done. + +==== Ignorelist and Blacklist +The ignorelist allows to automatically map licenses so that they are ignored in the further evaluation. The blacklist allows suppressing the automatic mapping of specific licenses. Both lists are configured via properties and are represented by a comma separated list of regular expressions. + +The default is: +---- +solicitor.scancode.automapping.blacklistpatterns=.*unknown.*,.*proprietary.* +solicitor.scancode.automapping.ignorelistpatterns= +---- +This prohibits automatic mapping of licenses ids which are ambiguous. No ignore mapping is done by default. + +==== Feature flag +The automatic mapping might be disabled by setting the corresponding feature flag to false: +---- +solicitor.feature-flag.scancode.automapping=false +---- + + === Correcting data The data obtained from ScanCode might be affected by false positives (wrongly detected a license or copyright) or false negatives (missed to detect a license or copyright). To compensate such defects there are two mechanisms: @@ -1496,6 +1524,7 @@ Using the <> it is possible to qualify whether a rul | _(empty)_ | ... in both cases |==================== +Due the automatic mapping of scancode based `RawLicenses` to `NormalizedLicenses` (see <>) such explicit mapping rules are only required for licenses not handled by the automatism. [appendix] == Default Base Configuration @@ -1641,6 +1670,7 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.19.0:: * https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. * https://github.com/devonfw/solicitor/issues/228: Extended `ApplicationComponent.dataStatus` values to reflect situation when no licenses were obtained from the Scancode information. See <>. +* https://github.com/devonfw/solicitor/issues/230: Enable automatic mapping of `RawLicense` data obtained from Scancode to `NormalizedLicenses`. See <>. Changes in 1.18.0:: * https://github.com/devonfw/solicitor/pull/225: Added some additional name mapping rules to handle SPDX-IDs and license references from Scancode. diff --git a/solicitor.dict b/solicitor.dict index 3c785fb2..acbc7e6c 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -23,6 +23,7 @@ licenseurls linux natively nd +NL ort oss RHS From 8157764a58fb2eeabc0e66198878c02dcc5227ce Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Sun, 11 Feb 2024 21:26:14 +0100 Subject: [PATCH 098/139] Generalize pattern of scancode licenses classified as WITH_ISSUES --- core/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index b3496110..299d6f27 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -63,7 +63,7 @@ solicitor.scancode.curations-filename=output/curations.yaml # base path of the file repo where sources and scancode information is stored solicitor.scancode.repo-base-path=output/Source # list of patterns of found licenses which shall set the dataStatus to WITH_ISSUES -solicitor.scancode.issuelistpatterns=LicenseRef-scancode-free-unknown +solicitor.scancode.issuelistpatterns=.*unknown.* # The curationDataSelector value to use when accessing curation data. # You can change its value to select a specific curation data source (if the implementation supports this). From ce1a0857d96807f724eff9485e436c0c46096e59 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Sun, 11 Feb 2024 21:36:04 +0100 Subject: [PATCH 099/139] Generalize pattern of scancode licenses classified as WITH_ISSUES (updated documentation) --- documentation/files/application.properties | 2 +- documentation/master-solicitor.asciidoc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/files/application.properties b/documentation/files/application.properties index b3496110..299d6f27 100644 --- a/documentation/files/application.properties +++ b/documentation/files/application.properties @@ -63,7 +63,7 @@ solicitor.scancode.curations-filename=output/curations.yaml # base path of the file repo where sources and scancode information is stored solicitor.scancode.repo-base-path=output/Source # list of patterns of found licenses which shall set the dataStatus to WITH_ISSUES -solicitor.scancode.issuelistpatterns=LicenseRef-scancode-free-unknown +solicitor.scancode.issuelistpatterns=.*unknown.* # The curationDataSelector value to use when accessing curation data. # You can change its value to select a specific curation data source (if the implementation supports this). diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 9199eb23..ac1654c3 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1671,6 +1671,7 @@ Changes in 1.19.0:: * https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. * https://github.com/devonfw/solicitor/issues/228: Extended `ApplicationComponent.dataStatus` values to reflect situation when no licenses were obtained from the Scancode information. See <>. * https://github.com/devonfw/solicitor/issues/230: Enable automatic mapping of `RawLicense` data obtained from Scancode to `NormalizedLicenses`. See <>. +* Generalize pattern of scancode licenses classified as WITH_ISSUES (from `LicenseRef-scancode-free-unknown` to `.\*unknown.*` ) Changes in 1.18.0:: * https://github.com/devonfw/solicitor/pull/225: Added some additional name mapping rules to handle SPDX-IDs and license references from Scancode. From d158030c946420f3dbe2b18e2e8177192e59b412 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:22:36 +0100 Subject: [PATCH 100/139] Version 1.19.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 4ce359a0..28e0a84b 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.19.0-SNAPSHOT + 1.19.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 182ee23b..bfb7ad18 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.19.0-SNAPSHOT + 1.19.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 3c1eb096..ce1cc430 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.19.0-SNAPSHOT + 1.19.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index ee72fe4d..a83795b6 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.19.0-SNAPSHOT + 1.19.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index ee09f049..c8d0b1f9 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.19.0-SNAPSHOT + 1.19.0 pom Solicitor Aggregator From 6927aa8188a3dc88076e9b937cc1c51c463cc450 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:29:26 +0100 Subject: [PATCH 101/139] Set version to SNAPSHOT of next minor release (1.20.0-SNAPSHOT) --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 28e0a84b..c2bdad33 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.19.0 + 1.20.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index bfb7ad18..dd6b4d20 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.19.0 + 1.20.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index ac1654c3..85ede1d8 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1667,6 +1667,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.20.0:: + Changes in 1.19.0:: * https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. * https://github.com/devonfw/solicitor/issues/228: Extended `ApplicationComponent.dataStatus` values to reflect situation when no licenses were obtained from the Scancode information. See <>. diff --git a/documentation/pom.xml b/documentation/pom.xml index ce1cc430..30b22cc9 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.19.0 + 1.20.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index a83795b6..add38e89 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.19.0 + 1.20.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index c8d0b1f9..e843b85b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.19.0 + 1.20.0-SNAPSHOT pom Solicitor Aggregator From d60fc0242b69437df334bfb821317f53bcdad01b Mon Sep 17 00:00:00 2001 From: duph97 Date: Wed, 21 Feb 2024 17:34:25 +0100 Subject: [PATCH 102/139] License name mapping ordering (#233) * Apply convention for ordering of LicenseNameMapping rules * Add release note --- .../rules/LicenseNameMappingSample.xls | Bin 198656 -> 202752 bytes documentation/master-solicitor.asciidoc | 1 + 2 files changed, 1 insertion(+) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls index 97739828e8b2e2f6567fc9a0810010c66e665ff6..47d7eb40b83233a4dd8922a7b6935ec0ab57b9f9 100644 GIT binary patch delta 16063 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 ZKi^l;?lnArp+AH_7c1uV*w|Wg{R;y-Rs#S4 diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 85ede1d8..bd4215bd 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1668,6 +1668,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.20.0:: +* https://github.com/devonfw/solicitor/issues/232: Set a standard for ordering LicenseNameMapping rules. Rules with an 'or-later' suffix are put before '-only' rules. Changes in 1.19.0:: * https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. From 1b2368d33493d91b343de9fa4ecfb00b851e9f05 Mon Sep 17 00:00:00 2001 From: malkam <108339027+mahmoudAlkam@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:36:01 +0100 Subject: [PATCH 103/139] Feature/missing fields datamodel (#236) * Correct handling of new data model fields in ModelImporterExporter. * adapt master-solicitor.asciidoc * fixed issue with test data * removed two duplicated lines --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../model/ModelImporterExporter.java | 50 +++++-- .../model/ModelImporterExporterTest.java | 40 +++++- .../resources/models/model_version_6.json | 124 ++++++++++++++++++ documentation/master-solicitor.asciidoc | 1 + 4 files changed, 205 insertions(+), 10 deletions(-) create mode 100644 core/src/test/resources/models/model_version_6.json diff --git a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java index 37152e77..f0ef849a 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/model/ModelImporterExporter.java @@ -34,7 +34,10 @@ import com.fasterxml.jackson.databind.SerializationFeature; /** - * Imports and exports the data model. + * The {@code ModelImporterExporter} class handles the import and export of the data model. It provides methods for + * loading a data model from a JSON file, checking model version compatibility, and saving the data model to a file. + * Additionally, it facilitates the transformation of JSON data into model objects, ensuring proper association between + * the various components of the data model. */ @Component public class ModelImporterExporter { @@ -54,10 +57,12 @@ public class ModelImporterExporter { private ModelFactoryImpl modelFactory; /** - * Loads the data model from a file. + * Loads the data model from a JSON file. The loaded data model is represented by a root object of type + * {@code ModelRootImpl}. * - * @param filename the name of the file to load from. - * @return the root object of the loaded data model + * @param filename the name of the file to load the data model from. + * @return the root object of the loaded data model. + * @throws SolicitorRuntimeException if there is an issue loading the data model from the file. */ public ModelRootImpl loadModel(String filename) { @@ -102,10 +107,11 @@ public ModelRootImpl loadModel(String filename) { } /** - * Checks if the version of the model to be loaded is supported. + * Checks if the version of the model to be loaded is supported and compatible with the current model version. * * @param readModelVersion the model version of the model to be loaded * @param currentModelRoot the root object of the current (target) model + * @throws SolicitorRuntimeException if the model version is unsupported or incompatible. */ private void checkModelVersion(int readModelVersion, ModelRootImpl currentModelRoot) { @@ -117,11 +123,13 @@ private void checkModelVersion(int readModelVersion, ModelRootImpl currentModelR } /** - * Read the {@link ApplicationComponent}s from the JSON data structure. + * Reads the {@link ApplicationComponent}s from the JSON data structure and populates the provided {@link Application} + * with the corresponding data. * * @param application the {@link Application} to which the {@link ApplicationComponent}s belong to * @param applicationComponentsNode the relevant part of the parse JSON model * @param readModelVersion the model version of the model to be read + * */ private void readApplicationComponents(ApplicationImpl application, JsonNode applicationComponentsNode, int readModelVersion) { @@ -148,6 +156,21 @@ private void readApplicationComponents(ApplicationImpl application, JsonNode app String noticeFileUrl = noticeFileUrlNode != null ? noticeFileUrlNode.asText(null) : null; JsonNode normalizedLicensesNode = applicationComponentNode.get("normalizedLicenses"); JsonNode rawLicensesNode = applicationComponentNode.get("rawLicenses"); + String dataStatus = applicationComponentNode.has("dataStatus") + ? applicationComponentNode.get("dataStatus").asText(null) + : null; + String traceabilityNotes = applicationComponentNode.has("traceabilityNotes") + ? applicationComponentNode.get("traceabilityNotes").asText(null) + : null; + String sourceDownloadUrl = applicationComponentNode.has("sourceDownloadUrl") + ? applicationComponentNode.get("sourceDownloadUrl").asText(null) + : null; + String packageDownloadUrl = applicationComponentNode.has("packageDownloadUrl") + ? applicationComponentNode.get("packageDownloadUrl").asText(null) + : null; + String noticeFileContentKey = applicationComponentNode.has("noticeFileContentKey") + ? applicationComponentNode.get("noticeFileContentKey").asText(null) + : null; ApplicationComponentImpl applicationComponent = this.modelFactory.newApplicationComponent(); applicationComponent.setApplication(application); @@ -165,6 +188,11 @@ private void readApplicationComponents(ApplicationImpl application, JsonNode app applicationComponent.setRepoType(repoType); applicationComponent.setCopyrights(copyrights); applicationComponent.setNoticeFileUrl(noticeFileUrl); + applicationComponent.setDataStatus(dataStatus); + applicationComponent.setTraceabilityNotes(traceabilityNotes); + applicationComponent.setSourceDownloadUrl(sourceDownloadUrl); + applicationComponent.setPackageDownloadUrl(packageDownloadUrl); + applicationComponent.setNoticeFileContentKey(noticeFileContentKey); readNormalizedLicenses(applicationComponent, normalizedLicensesNode, readModelVersion); readRawLicenses(applicationComponent, rawLicensesNode, readModelVersion); @@ -226,7 +254,8 @@ private void readEngagement(ModelRootImpl modelRoot, JsonNode engagementNode, in } /** - * Read the {@link NormalizedLicense}s from the JSON data structure. + * Reads the {@link NormalizedLicense}s from the JSON data structure and associates them with the provided + * {@link ApplicationComponent}. * * @param applicationComponent The {@link ApplicationComponent} to which the license belongs * @param normalizedLicensesNode the relevant part of the parsed JSON model @@ -236,6 +265,7 @@ private void readNormalizedLicenses(ApplicationComponentImpl applicationComponen int readModelVersion) { for (JsonNode normalizedLicenseNode : normalizedLicensesNode) { + // Extracting information from the JSON node String declaredLicense = normalizedLicenseNode.get("declaredLicense").asText(null); String licenseUrl = normalizedLicenseNode.get("licenseUrl").asText(null); String normalizedLicenseType = normalizedLicenseNode.get("normalizedLicenseType").asText(null); @@ -261,6 +291,7 @@ private void readNormalizedLicenses(ApplicationComponentImpl applicationComponen guessedLicenseUrl = normalizedLicenseNode.get("guessedLicenseUrl").asText(null); guessedLicenseUrlAuditInfo = normalizedLicenseNode.get("guessedLicenseUrlAuditInfo").asText(null); } + // Text pool keys introduced in certain model versions String effectiveNormalizedLicenseContentKey = null; String declaredLicenseContentKey = null; String licenseRefContentKey = null; @@ -274,7 +305,7 @@ private void readNormalizedLicenses(ApplicationComponentImpl applicationComponen normalizedLicenseContentKey = normalizedLicenseNode.get("normalizedLicenseContentKey").asText(null); guessedLicenseContentKey = normalizedLicenseNode.get("guessedLicenseContentKey").asText(null); } - + // Creating a new NormalizedLicense object and populating its fields NormalizedLicenseImpl normalizedLicense = this.modelFactory.newNormalizedLicense(); normalizedLicense.setApplicationComponent(applicationComponent); normalizedLicense.setDeclaredLicense(declaredLicense); @@ -303,6 +334,7 @@ private void readNormalizedLicenses(ApplicationComponentImpl applicationComponen normalizedLicense.setLicenseRefContentKey(licenseRefContentKey); normalizedLicense.setNormalizedLicenseContentKey(normalizedLicenseContentKey); normalizedLicense.setGuessedLicenseContentKey(guessedLicenseContentKey); + } } @@ -331,7 +363,6 @@ private void readRawLicenses(ApplicationComponentImpl applicationComponent, Json rawLicense.setTrace(trace); rawLicense.setOrigin(origin); rawLicense.setSpecialHandling(specialHandling); - } } @@ -349,6 +380,7 @@ private void readTextPool(ModelRootImpl modelRoot, JsonNode textPoolNode, int re } TextPool textPool = modelRoot.getTextPool(); JsonNode dataMapNode = textPoolNode.get("dataMap"); + for (JsonNode singleEntryValue : dataMapNode) { // only store values; keys will be reconstructed based on values textPool.store(singleEntryValue.asText()); diff --git a/core/src/test/java/com/devonfw/tools/solicitor/model/ModelImporterExporterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/model/ModelImporterExporterTest.java index 3287099b..492a3e71 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/model/ModelImporterExporterTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/model/ModelImporterExporterTest.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -9,6 +10,9 @@ import org.springframework.boot.test.context.SpringBootTest; import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.Engagement; /** * Tests of {@link ModelImporterExporter}. @@ -68,6 +72,40 @@ public void testLoadModelVersion5() { this.mie.loadModel("src/test/resources/models/model_version_5.json"); } + /** + * Test method for {@link com.devonfw.tools.solicitor.model.ModelImporterExporter#loadModel(java.lang.String)}. + */ + @Test + public void testLoadModelVersion6() { + + ModelRoot modelRoot = this.mie.loadModel("src/test/resources/models/model_version_6.json"); + + // Assert + Assertions.assertNotNull(modelRoot); + Engagement engagement = modelRoot.getEngagement(); + Assertions.assertNotNull(engagement); + List applications = engagement.getApplications(); + Assertions.assertFalse(applications.isEmpty()); + + Application application = applications.get(0); + Assertions.assertNotNull(application); + + List applicationComponents = application.getApplicationComponents(); + Assertions.assertFalse(applicationComponents.isEmpty()); + + ApplicationComponent component = applicationComponents.get(0); + Assertions.assertNotNull(component); + + // Add assertions for specific fields + Assertions.assertEquals("DA:NO_ISSUES", component.getDataStatus()); + Assertions.assertEquals("", component.getTraceabilityNotes()); // Assuming it's an empty string + Assertions.assertEquals("https://github.com/qos-ch/logback/archive/refs/tags/logback-1.2.3.zip", + component.getSourceDownloadUrl()); + Assertions.assertEquals( + "https://repo.maven.apache.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar", + component.getPackageDownloadUrl()); + } + /** * Test method for * {@link com.devonfw.tools.solicitor.model.ModelImporterExporter#saveModel(com.devonfw.tools.solicitor.model.ModelRoot, java.lang.String)}. @@ -77,7 +115,7 @@ public void testLoadModelVersion5() { @Test public void testSaveModel() throws IOException { - ModelRoot mr = this.mie.loadModel("src/test/resources/models/model_version_3.json"); + ModelRoot mr = this.mie.loadModel("src/test/resources/models/model_version_6.json"); File tempFile = File.createTempFile("solicitor_model", "json"); String fileName = tempFile.getPath(); diff --git a/core/src/test/resources/models/model_version_6.json b/core/src/test/resources/models/model_version_6.json new file mode 100644 index 00000000..818bfcea --- /dev/null +++ b/core/src/test/resources/models/model_version_6.json @@ -0,0 +1,124 @@ +{ + "executionTime" : "Fri Oct 01 18:38:31 CEST 2021", + "modelVersion" : 6, + "solicitorVersion" : "1.3.0-SNAPSHOT", + "solicitorGitHash" : "a507f22", + "solicitorBuilddate" : "2021-10-01 18:34:12 +0200", + "extensionArtifactId" : "cap-solicitor-extension", + "extensionVersion" : "1.3.0-RC1", + "extensionGitHash" : "043a961", + "extensionBuilddate" : "2021-09-13 22:24:45", + "engagement" : { + "engagementName" : "Some Engagement", + "engagementType" : "INTERN", + "clientName" : "none", + "goToMarketModel" : "LICENSE", + "contractAllowsOss" : true, + "ossPolicyFollowed" : true, + "customerProvidesOss" : false, + "applications" : [ { + "name" : "Some Application", + "releaseId" : "1.2.3-SNAPSHOT", + "releaseDate" : "-UNDEFINED-", + "sourceRepo" : "https://point/to/your/repo.git", + "programmingEcosystem" : "Java8", + "applicationComponents" : [ { + "usagePattern" : "DYNAMIC_LINKING", + "ossModified" : false, + "ossHomepage" : null, + "sourceRepoUrl" : "https://github.com/qos-ch/logback", + "noticeFileUrl" : "http://some.url", + "noticeFileContentKey" : "31cb574375eadadae8835a406879719d85da9fbb5f0f0c6fdc62da741dc49de5", + "groupId" : "ch.qos.logback", + "artifactId" : "logback-classic", + "version" : "1.2.3", + "repoType" : "maven", + "packageUrl" : "pkg:maven/ch.qos.logback/logback-classic@1.2.3", + "copyrights" : null, + "packageDownloadUrl" : "https://repo.maven.apache.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar", + "sourceDownloadUrl" : "https://github.com/qos-ch/logback/archive/refs/tags/logback-1.2.3.zip", + "normalizedLicenses" : [ { + "declaredLicense" : "Eclipse Public License - v 1.0", + "licenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "declaredLicenseContentKey" : "cf630ee3446f9e10c1b0814fb8a652f4733b491eaea9d5d1f89f803daaaa6075", + "normalizedLicenseType" : "OSS-SPDX", + "normalizedLicense" : "EPL-1.0", + "normalizedLicenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "normalizedLicenseContentKey" : "cf630ee3446f9e10c1b0814fb8a652f4733b491eaea9d5d1f89f803daaaa6075", + "effectiveNormalizedLicenseType" : "IGNORE", + "effectiveNormalizedLicense" : "Ignore", + "effectiveNormalizedLicenseUrl" : null, + "effectiveNormalizedLicenseContentKey" : "9d086420212f290c944042014500f00716c30d53f316d224b4cdd0c20756ab6b", + "legalPreApproved" : "N/A", + "copyLeft" : "N/A", + "licenseCompliance" : "N/A", + "licenseRefUrl" : "http://another.url", + "licenseRefContentKey" : "7dbd9399a4684cdc5c1c51d6359294caea160c648f1792e273a9489e46321b88", + "includeLicense" : "no", + "includeSource" : "no", + "reviewedForRelease" : null, + "comments" : null, + "legalApproved" : "N/A", + "legalComments" : null, + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'\r\n+ Rule Group: LicenseNameMappingDefaults; RuleId: 57; Matching: declaredLicense==Eclipse Public License - v 1.0; Setting: normalizedLicenseType=OSS-SPDX, normalizedLicense=EPL-1.0, normalizedLicenseUrl=http://www.eclipse.org/legal/epl-v10.html (taking data from input)\r\n+ Rule Group: MultiLicenseSelection; RuleId: 6; Matching: groupId==ch.qos.logback, normalizedLicense==EPL-1.0; Setting: effectiveNormalizedLicenseType=IGNORE (multilicensing: ignore this, prefer LGPL-2.1), effectiveNormalizedLicense=Ignore\r\n+ Rule Group: LegalPreEvaluation; RuleId: 29; Matching: effectiveNormalizedLicenseType==IGNORE; Setting: legalPreApproved=N/A, copyLeft=N/A, licenseCompliance=N/A, includeLicense=no, includeSource=no\r\n+ Rule Group: LegalEvaluation; RuleId: 1; Matching: effectiveNormalizedLicenseType==IGNORE; Setting: legalApproved=N/A", + "guessedLicenseUrl" : null, + "guessedLicenseContentKey" : "90fa1cb2ff3629e0f7a3c78dc887134e5e05224de1b13fed1dedb33b1e920724", + "guessedLicenseUrlAuditInfo" : null + }, { + "declaredLicense" : "GNU Lesser General Public License", + "licenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "declaredLicenseContentKey" : "cf630ee3446f9e10c1b0814fb8a652f4733b491eaea9d5d1f89f803daaaa6075", + "normalizedLicenseType" : "OSS-SPDX", + "normalizedLicense" : "LGPL-2.1", + "normalizedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "normalizedLicenseContentKey" : "cf630ee3446f9e10c1b0814fb8a652f4733b491eaea9d5d1f89f803daaaa6075", + "effectiveNormalizedLicenseType" : "OSS-SPDX", + "effectiveNormalizedLicense" : "LGPL-2.1", + "effectiveNormalizedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "effectiveNormalizedLicenseContentKey" : "cf630ee3446f9e10c1b0814fb8a652f4733b491eaea9d5d1f89f803daaaa6075", + "legalPreApproved" : "no", + "copyLeft" : "weak", + "licenseCompliance" : "check legal", + "licenseRefUrl" : "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt", + "licenseRefContentKey" : "7dbd9399a4684cdc5c1c51d6359294caea160c648f1792e273a9489e46321b88", + "includeLicense" : "yes", + "includeSource" : "yes", + "reviewedForRelease" : null, + "comments" : null, + "legalApproved" : "Conditional", + "legalComments" : "OK, in case of dynamic linking.", + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'\r\n+ Rule Group: LicenseNameMappingDefaults; RuleId: 101; Matching: licenseUrl==REGEX:https?://www.gnu.org/licenses/old-licenses/lgpl-2.1.*; Setting: normalizedLicenseType=OSS-SPDX, normalizedLicense=LGPL-2.1, normalizedLicenseUrl=http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html (taking data from input)\r\n+ Rule Group: LicenseSelection; RuleId: DEFAULT; Matching: -default-; Setting: effectiveNormalizedLicenseType=OSS-SPDX (taking data from input), effectiveNormalizedLicense=LGPL-2.1 (taking data from input), effectiveNormalizedLicenseUrl=http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html (taking data from input)\r\n+ Rule Group: LegalPreEvaluation; RuleId: 6; Matching: effectiveNormalizedLicenseType==OSS-SPDX, effectiveNormalizedLicense==LGPL-2.1; Setting: legalPreApproved=no, copyLeft=weak, licenseCompliance=check legal, licenseRefUrl=https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt, includeLicense=yes, includeSource=yes\r\n+ Rule Group: LegalEvaluation; RuleId: 14; Matching: usagePattern==DYNAMIC_LINKING, effectiveNormalizedLicenseType==OSS-SPDX, effectiveNormalizedLicense==LGPL-2.1; Setting: legalApproved=Conditional, legalComments=OK, in case of dynamic linking.", + "guessedLicenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "guessedLicenseContentKey" : "90fa1cb2ff3629e0f7a3c78dc887134e5e05224de1b13fed1dedb33b1e920724", + "guessedLicenseUrlAuditInfo" : "" + } ], + "rawLicenses" : [ { + "declaredLicense" : "Eclipse Public License - v 1.0", + "licenseUrl" : "http://www.eclipse.org/legal/epl-v10.html", + "declaredLicenseContentKey" : null, + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'", + "origin" : "scancode", + "specialHandling" : true + }, { + "declaredLicense" : "GNU Lesser General Public License", + "licenseUrl" : "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html", + "declaredLicenseContentKey" : null, + "trace" : "+ Component/License info read in '[maven]' format from 'file:./input/licenses_starter.xml'", + "origin" : "scancode", + "specialHandling" : true + } ], + "dataStatus" : "DA:NO_ISSUES", + "traceabilityNotes" : "" + } ] + } ] + }, + "textPool" : { + "dataMap" : { + "31cb574375eadadae8835a406879719d85da9fbb5f0f0c6fdc62da741dc49de5" : "Some content of a NOTICE file", + "7dbd9399a4684cdc5c1c51d6359294caea160c648f1792e273a9489e46321b88" : "Even some other license text", + "90fa1cb2ff3629e0f7a3c78dc887134e5e05224de1b13fed1dedb33b1e920724" : "Some license text", + "9d086420212f290c944042014500f00716c30d53f316d224b4cdd0c20756ab6b" : "Yet another license text", + "cf630ee3446f9e10c1b0814fb8a652f4733b491eaea9d5d1f89f803daaaa6075" : "Some other license text" + } + } +} \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index bd4215bd..456796e8 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1669,6 +1669,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.20.0:: * https://github.com/devonfw/solicitor/issues/232: Set a standard for ordering LicenseNameMapping rules. Rules with an 'or-later' suffix are put before '-only' rules. +* https://github.com/devonfw/solicitor/issues/234: Correct handling of new data model fields in ModelImporterExporter `dataStatus`,`traceabilityNotes` etc. Changes in 1.19.0:: * https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. From ccc6fc181b5c9d856e219a304cc76791eb0a2f4d Mon Sep 17 00:00:00 2001 From: malkam <108339027+mahmoudAlkam@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:44:57 +0100 Subject: [PATCH 104/139] Support suppressing to apply curations (#235) * Generate Curation Template without applied curations * update test and master-solicitor.asciidoc * adapt javadoc * update after Review * update javadoc * update javadoc * adapt javadoc in FilteredScancodeComponentInfoProvider.java * Documentation improvements, added a unit test for SingleFileCurationProvider * Fixed typo in Asciidoc --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../ComponentInfoInventoryProcessor.java | 3 +- .../curation/ComponentInfoCuratorImpl.java | 3 +- .../CuratingComponentInfoAdapter.java | 4 +- .../curation/CurationProvider.java | 10 +-- .../curation/SingleFileCurationProvider.java | 10 ++- ...FilteredScancodeComponentInfoProvider.java | 16 +++-- .../src/main/resources/application.properties | 1 + .../SingleFileCurationProviderTest.java | 68 +++++++++++++++++++ documentation/files/application.properties | 1 + documentation/master-solicitor.asciidoc | 1 + 10 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index c174b47d..0368b880 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -75,7 +75,8 @@ public void add(Statistics statistics) { * Set the curationDataSelector. * * @param curationDataSelector the curationDataSelector to chose when getting curation data. An empty string will be - * stored as null null which results in the default origin being taken. + * stored as null null which results in the default origin being taken. Use "none" to indicate no + * curation to be applied. */ @Value("${solicitor.curationDataSelector}") public void setCurationDataSelector(String curationDataSelector) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java index ada9a5aa..036b4650 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java @@ -49,7 +49,8 @@ public ComponentInfoCuratorImpl(CurationProvider curationProvider, * * @param componentInfo the componentInfo to curate * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. + * indicates that the default should be used. The special value "none" indicates that no curations will be + * applied. * @return the curated component info * @throws ComponentInfoAdapterException if the curation could not be read */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java index 4ce22feb..c44c24de 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java @@ -50,8 +50,8 @@ public CuratingComponentInfoAdapter(FilteredComponentInfoProvider filteredCompon * data as a {@link ComponentInfo} object. * * @param packageUrl The identifier of the package for which information is requested - * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. + * @param curationDataSelector Identifies which source should be used for the curation data. null + * indicates that the default should be used. Use "none" to indicate that no curation should be applied. * @return the data derived from the scancode results after applying any defined curation. * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be return in such a case. diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java index be9377ca..8f2d9988 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java @@ -15,11 +15,13 @@ public interface CurationProvider { * * @param packageUrl identifies the package * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. - * @return the curation data if it exists or null if no curation exist for the package. + * indicates that the default should be used. The special value "none" indicates that no curations should be + * returned. + * @return the curation data if it exists. null if no curation exist for the package or the + * curationDataSelector was given as "none". * @throws ComponentInfoAdapterException if something unexpected happens - * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException if the specified curationDataSelector could not be - * resolved + * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException if the specified curationDataSelector could + * not be resolved */ ComponentInfoCuration findCurations(String packageUrl, String curationDataSelector) throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java index 169ec7c2..9667a828 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java @@ -56,14 +56,20 @@ public SingleFileCurationProvider(AllKindsPackageURLHandler packageURLHandler) { * Return the curation data for a given package. * * @param packageUrl identifies the package - * @param curationDataSelector identifies which source should be used for the curation data. This parameter is ignored - * by this implementation. + * @param curationDataSelector identifies which source should be used for the curation data. The value "none" + * indicates that no curations should be returned. + * @return the curation data if it exists. null if no curation exist for the package or the + * curationDataSelector was given as "none". * @throws ComponentInfoAdapterException if something unexpected happens */ @Override public ComponentInfoCuration findCurations(String packageUrl, String curationDataSelector) throws ComponentInfoAdapterException { + // Return null if curationDataSelector is "none" + if ("none".equalsIgnoreCase(curationDataSelector)) { + return null; + } ComponentInfoCuration foundCuration = null; String packagePathPart = this.packageURLHandler.pathFor(packageUrl); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java index a84ad589..4660d3a4 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java @@ -94,10 +94,10 @@ public void setMinLicensefileNumberOfLines(int minLicensefileNumberOfLines) { * * @param packageUrl The package url of the package * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. + * indicates that the default should be used. "none" indicates that no curations should be applied. * @return the read scancode information * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no - * data available no exception will be thrown. Instead null will be return in such a case. + * data available no exception will be thrown. Instead null will be returned in such a case. */ @Override public ComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) @@ -136,10 +136,14 @@ private void addSupplementedData(ScancodeRawComponentInfo rawScancodeData, } /** - * @param packageUrl - * @param rawScancodeData - * @return - * @throws ComponentInfoAdapterException + * Parses and maps scancode JSON to create ScancodeComponentInfo. + * + * @param packageUrl package URL of the package + * @param rawScancodeData raw scancode data + * @param curationDataSelector identifies which source should be used for the curation data. If the value of + * curationdataselector equals "none," no curations will be applied. + * @return the ScancodeComponentInfo + * @throws ComponentInfoAdapterException if there was an issue during parsing */ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, ScancodeRawComponentInfo rawScancodeData, String curationDataSelector) throws ComponentInfoAdapterException { diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index 299d6f27..901ec800 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -67,6 +67,7 @@ solicitor.scancode.issuelistpatterns=.*unknown.* # The curationDataSelector value to use when accessing curation data. # You can change its value to select a specific curation data source (if the implementation supports this). +# Set it to "none" to skip applying curations (no curations will be applied). # Leave it empty to use the default curation data source. solicitor.curationDataSelector= diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java new file mode 100644 index 00000000..de1f8e06 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java @@ -0,0 +1,68 @@ +package com.devonfw.tools.solicitor.componentinfo.curating; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; + +/** + * Test for {@link SingleFileCurationProvider}. + * + */ +class SingleFileCurationProviderTest { + + private SingleFileCurationProvider objectUnderTest; + + @BeforeEach + public void setup() { + + AllKindsPackageURLHandler packageUrlHandler = Mockito.mock(AllKindsPackageURLHandler.class); + Mockito.when(packageUrlHandler.pathFor("pkg:maven/somenamespace/somecomponent@2.3.4")) + .thenReturn("pkg/maven/somenamespace/somecomponent/2.3.4"); + Mockito.when(packageUrlHandler.pathFor("pkg:maven/somenamespace/somecomponent@2.3.5")) + .thenReturn("pkg/maven/somenamespace/somecomponent/2.3.5"); + + this.objectUnderTest = new SingleFileCurationProvider(packageUrlHandler); + this.objectUnderTest.setCurationsFileName("src/test/resources/curations/array_of_curations.yaml"); + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider#findCurations(java.lang.String, java.lang.String)}. + * + * @throws ComponentInfoAdapterException + */ + @Test + void testFindCurationsWithSelectorNull() throws ComponentInfoAdapterException { + + ComponentInfoCuration result; + + result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.4", null); + assertEquals("https://scancode-licensedb.aboutcode.org/apache-2.0.LICENSE", result.getLicenses().get(0).getUrl()); + result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.5", null); + assertEquals("https://scancode-licensedb.aboutcode.org/bsd-simplified.LICENSE", + result.getLicenses().get(0).getUrl()); + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider#findCurations(java.lang.String, java.lang.String)}. + * + * @throws ComponentInfoAdapterException + */ + @Test + void testFindCurationsWithSelectorNone() throws ComponentInfoAdapterException { + + ComponentInfoCuration result; + + result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.4", "none"); + assertNull(result); + } +} diff --git a/documentation/files/application.properties b/documentation/files/application.properties index 299d6f27..901ec800 100644 --- a/documentation/files/application.properties +++ b/documentation/files/application.properties @@ -67,6 +67,7 @@ solicitor.scancode.issuelistpatterns=.*unknown.* # The curationDataSelector value to use when accessing curation data. # You can change its value to select a specific curation data source (if the implementation supports this). +# Set it to "none" to skip applying curations (no curations will be applied). # Leave it empty to use the default curation data source. solicitor.curationDataSelector= diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 456796e8..9d8f74ab 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1670,6 +1670,7 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.20.0:: * https://github.com/devonfw/solicitor/issues/232: Set a standard for ordering LicenseNameMapping rules. Rules with an 'or-later' suffix are put before '-only' rules. * https://github.com/devonfw/solicitor/issues/234: Correct handling of new data model fields in ModelImporterExporter `dataStatus`,`traceabilityNotes` etc. +* https://github.com/devonfw/solicitor/pull/235: Improvements in Curation Data Handling. When the curationDataSelector parameter is set to "none," no curations will be applied. Changes in 1.19.0:: * https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. From fed2f9dfc46dc2124d87ec92281354bb905a6fab Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:15:45 +0100 Subject: [PATCH 105/139] Implementation of YarnModernReader to support Yarn 2 and above (#238) --- .../tools/solicitor/common/LogMessages.java | 6 +- .../reader/yarnmodern/YarnModernReader.java | 181 ++++++++++++++++++ .../yarnmodern/YarnModernReaderTests.java | 121 ++++++++++++ core/src/test/resources/yarnModernReport.json | 33 ++++ documentation/files/yarnmodernlicenses.json | 10 + documentation/master-solicitor.asciidoc | 34 +++- 6 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReader.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReaderTests.java create mode 100644 core/src/test/resources/yarnModernReport.json create mode 100644 documentation/files/yarnmodernlicenses.json diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 023bc065..7c7a112e 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -99,7 +99,11 @@ public enum LogMessages { + "skipped due to unkown SPDX: {}, mapped using type SCANCODE: {}, mapped using type OSS-SPDX: {}, mapped to IGNORE: {}"), // NOT_A_VALID_NPM_PACKAGE_NAME(67, "{} is not a valid name for an NPM package"), // SCANCODE_ISSUE_DETECTION_REGEX(68, - "The list of regular expressions for detecting licenses from scancode having issues is set to '{}'"); + "The list of regular expressions for detecting licenses from scancode having issues is set to '{}'"), // + MODERN_YARN_VIRTUAL_PACKAGE(69, + "When reading yarn license info from file '{}' there was at least one virtual package encountered. Check if package resolution is correct"), // + MODERN_YARN_PATCHED_PACKAGE(70, + "When reading yarn license info from file '{}' there was at least one patched package encountered. Processing only the base package, not the patched version."); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReader.java new file mode 100644 index 00000000..1ca903b4 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReader.java @@ -0,0 +1,181 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.devonfw.tools.solicitor.reader.yarnmodern; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.PackageURLHelper; +import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; +import com.devonfw.tools.solicitor.reader.AbstractReader; +import com.devonfw.tools.solicitor.reader.Reader; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * A {@link Reader} which reads data generated by + * yarn-plugin-licenses. + */ + +@Component +public class YarnModernReader extends AbstractReader implements Reader { + + private static final Logger LOG = LoggerFactory.getLogger(YarnModernReader.class); + + /** + * The supported type of this {@link Reader}. + */ + public static final String SUPPORTED_TYPE = "yarn-modern"; + + /** {@inheritDoc} */ + @Override + public Set getSupportedTypes() { + + return Collections.singleton(SUPPORTED_TYPE); + } + + /** {@inheritDoc} */ + @SuppressWarnings("rawtypes") + @Override + public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, + String repoType, Map configuration) { + + String content = readAndPreprocessJson(sourceUrl); + + int componentCount = 0; + int licenseCount = 0; + + ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + List body; + try { + body = mapper.readValue(content, List.class); + } catch (IOException e) { + throw new SolicitorRuntimeException("Could not read yarn modern inventory source '" + sourceUrl + "'", e); + } + for (int i = 0; i < body.size(); i++) { + Map licenseBlock = (Map) body.get(i); + + String license = licenseBlock.get("value"); + List locators = new ArrayList<>(); + + for (Map.Entry entry : licenseBlock.entrySet()) { + if (entry.getKey().endsWith(".value.locator")) { + locators.add(entry.getValue()); + } + } + + for (String locator : locators) { + int lastAtPosition = locator.lastIndexOf("@"); + String name = locator.substring(0, lastAtPosition); + String version = locator.substring(lastAtPosition + 1); + String repo = licenseBlock.get("children." + locator + ".children.url"); + String licenseUrl = defaultGithubLicenseURL(repo); + String vendorUrl = licenseBlock.get("children." + locator + ".children.vendorUrl"); + String homePage = ""; + if (vendorUrl != null) { + homePage = vendorUrl; + } + + ApplicationComponent appComponent = getModelFactory().newApplicationComponent(); + appComponent.setApplication(application); + componentCount++; + appComponent.setArtifactId(name); + appComponent.setVersion(version); + appComponent.setUsagePattern(usagePattern); + appComponent.setGroupId(""); + appComponent.setOssHomepage(homePage); + appComponent.setSourceRepoUrl(repo); + appComponent.setRepoType(repoType); + appComponent.setPackageUrl(PackageURLHelper.fromNpmPackageNameAndVersion(name, version).toString()); + + addRawLicense(appComponent, license, licenseUrl, sourceUrl); + licenseCount++; + } + } + doLogging(sourceUrl, application, componentCount, licenseCount); + + } + + // helper method that defaults github-links (html) to a default license path + private String defaultGithubLicenseURL(String repo) { + + if (repo == null) { + return null; + } + + if (repo.contains("github.com") && !repo.contains("/raw/")) { + repo = repo.replace("git://", "https://"); + repo = repo.replace("github.com", "raw.githubusercontent.com"); + repo = repo.concat("/master/LICENSE"); + } + return repo; + } + + // helper method that extracts information from the .json created by yarn + // licenses into a correct form + private String readAndPreprocessJson(String sourceURL) { + + String filePath = sourceURL.replaceAll("file:", ""); + File input = new File(filePath); + String content = ""; + + try { + BufferedReader reader = new BufferedReader(new FileReader(input)); + String line = null; + StringBuilder sb = new StringBuilder(); + + // read in the complete file + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + + content = sb.toString(); + + // patch data of virtual and patched packages to conform to "normal" packages + String oldContent = content; + content = content.replaceAll("virtual:.*?#", ""); + if (!content.equals(oldContent)) { + LOG.warn(LogMessages.MODERN_YARN_VIRTUAL_PACKAGE.msg(), sourceURL); + } + oldContent = content; + content = content.replaceAll("@patch:.*?@npm%3A(.*?)#.*?(.value.|.children.|\")", "@npm:$1$2"); + if (!content.equals(oldContent)) { + LOG.warn(LogMessages.MODERN_YARN_PATCHED_PACKAGE.msg(), sourceURL); + } + content = content.replaceAll("@workspace:\\.", "@none"); + content = content.replace("@npm:", "@"); + // fixes URL issues + content = content.replace("git+", ""); + content = content.replace("www.github", "github"); + content = content.replace(".git", ""); + content = content.replace("git://", "https://"); + content = content.replace("git@github.com:", "https://github.com/"); + content = content.replace("ssh://git@", "https://"); + content = content.replace("Unknown", ""); + + reader.close(); + + } catch (IOException e) { + throw new SolicitorRuntimeException("Could not read yarn inventory source '" + sourceURL + "'", e); + } + return content; + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReaderTests.java new file mode 100644 index 00000000..dc02828e --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReaderTests.java @@ -0,0 +1,121 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.devonfw.tools.solicitor.reader.yarnmodern; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.tools.solicitor.common.FileInputStreamFactory; +import com.devonfw.tools.solicitor.model.ModelFactory; +import com.devonfw.tools.solicitor.model.impl.ModelFactoryImpl; +import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; +import com.devonfw.tools.solicitor.model.masterdata.Application; +import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; + +/** + * Tests for {@link YarnModernReader}. + */ +public class YarnModernReaderTests { + private static final Logger LOG = LoggerFactory.getLogger(YarnModernReaderTests.class); + + Application application; + + /** + * The constructor. + */ + public YarnModernReaderTests() { + + ModelFactory modelFactory = new ModelFactoryImpl(); + + this.application = modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com", "Angular"); + YarnModernReader yr = new YarnModernReader(); + yr.setModelFactory(modelFactory); + yr.setInputStreamFactory(new FileInputStreamFactory()); + yr.readInventory("yarn-modern", "src/test/resources/yarnModernReport.json", this.application, + UsagePattern.STATIC_LINKING, "npm", null); + + } + + /** + * Check the read in data. + */ + @Test + public void findArtifact() { + + assertArtifactExists("@babel/runtime", // + "6.15.7", // + "pkg:npm/%40babel/runtime@6.15.7", // + "MIT", "https://babel.dev/docs/en/next/babel-runtime", // + "https://github.com/babel/babel", // + "https://raw.githubusercontent.com/babel/babel/master/LICENSE"); + assertArtifactExists("@floating-ui/react-dom", // + "2.2.0", // + "pkg:npm/%40floating-ui/react-dom@2.2.0", // + "MIT", "https://floating-ui.com/docs/react-dom", // + "https://github.com/floating-ui/floating-ui", // + "https://raw.githubusercontent.com/floating-ui/floating-ui/master/LICENSE"); + assertArtifactExists("@humanwhocodes/config-array", // + "0.9.17", // + "pkg:npm/%40humanwhocodes/config-array@0.9.17", // + "Apache-2.0", // + "https://github.com/humanwhocodes/config-array#readme", // + "https://github.com/humanwhocodes/config-array", // + "https://raw.githubusercontent.com/humanwhocodes/config-array/master/LICENSE"); + assertArtifactExists("typescript", // + "4.3.7", // + "pkg:npm/typescript@4.3.7", // + "Apache-2.0", // + "https://www.typescriptlang.org/", // + "https://github.com/Microsoft/TypeScript", // + "https://raw.githubusercontent.com/Microsoft/TypeScript/master/LICENSE"); + assertArtifactExists("my-space", // + "none", // + "pkg:npm/my-space@none", // + "UNKNOWN", // + "", // + null, // + null); + } + + private void assertArtifactExists(String artifactId, String version, String packageUrl, String license, + String ossHomepage, String sourceRepoUrl, String licenseUrl) { + + List lapc = this.application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals(artifactId) && // + ap.getVersion().equals(version)) { + found = true; + assertEquals(packageUrl, ap.getPackageUrl()); + assertEquals(license, ap.getRawLicenses().get(0).getDeclaredLicense()); + assertEquals(ossHomepage, ap.getOssHomepage()); + assertEquals(sourceRepoUrl, ap.getSourceRepoUrl()); + assertEquals(licenseUrl, ap.getRawLicenses().get(0).getLicenseUrl()); + assertEquals("npm", ap.getRepoType()); + assertEquals(UsagePattern.STATIC_LINKING, ap.getUsagePattern()); + + break; + } + } + assertTrue(found); + } + + /** + * Test if the number of read components is correct. + */ + @Test + public void readFileAndCheckSize() { + + LOG.info(this.application.toString()); + assertEquals(5, this.application.getApplicationComponents().size()); + } + +} diff --git a/core/src/test/resources/yarnModernReport.json b/core/src/test/resources/yarnModernReport.json new file mode 100644 index 00000000..753f4ff1 --- /dev/null +++ b/core/src/test/resources/yarnModernReport.json @@ -0,0 +1,33 @@ +[ + { + "value": "MIT", + "children.@babel/runtime@npm:6.15.7.value.locator": "@babel/runtime@npm:6.15.7", + "children.@babel/runtime@npm:6.15.7.value.descriptor": "@babel/runtime@npm:^6.17.7", + "children.@babel/runtime@npm:6.15.7.children.url": "https://github.com/babel/babel.git", + "children.@babel/runtime@npm:6.15.7.children.vendorName": "The Babel Team", + "children.@babel/runtime@npm:6.15.7.children.vendorUrl": "https://babel.dev/docs/en/next/babel-runtime", + "children.@floating-ui/react-dom@virtual:3144d465a5f41fdd2d427886c8d853d1294ea068e6d3ea0018fc9787793599db665d173a4e52d32010c4b3e07a91aa0eeac00cbb00a677214be83ab38f3b1afc#npm:2.2.0.value.locator": "@floating-ui/react-dom@virtual:3144d465a5f41fdd2d427886c8d853d1294ea068e6d3ea0018fc9787793599db665d173a4e52d32010c4b3e07a91aa0eeac00cbb00a677214be83ab38f3b1afc#npm:2.2.0", + "children.@floating-ui/react-dom@virtual:3144d465a5f41fdd2d427886c8d853d1294ea068e6d3ea0018fc9787793599db665d173a4e52d32010c4b3e07a91aa0eeac00cbb00a677214be83ab38f3b1afc#npm:2.2.0.value.descriptor": "@floating-ui/react-dom@virtual:3144d465a5f41fdd2d427886c8d853d1294ea068e6d3ea0018fc9787793599db665d173a4e52d32010c4b3e07a91aa0eeac00cbb00a677214be83ab38f3b1afc#npm:^2.0.0", + "children.@floating-ui/react-dom@virtual:3144d465a5f41fdd2d427886c8d853d1294ea068e6d3ea0018fc9787793599db665d173a4e52d32010c4b3e07a91aa0eeac00cbb00a677214be83ab38f3b1afc#npm:2.2.0.children.url": "https://github.com/floating-ui/floating-ui.git", + "children.@floating-ui/react-dom@virtual:3144d465a5f41fdd2d427886c8d853d1294ea068e6d3ea0018fc9787793599db665d173a4e52d32010c4b3e07a91aa0eeac00cbb00a677214be83ab38f3b1afc#npm:2.2.0.children.vendorName": "atomiks", + "children.@floating-ui/react-dom@virtual:3144d465a5f41fdd2d427886c8d853d1294ea068e6d3ea0018fc9787793599db665d173a4e52d32010c4b3e07a91aa0eeac00cbb00a677214be83ab38f3b1afc#npm:2.2.0.children.vendorUrl": "https://floating-ui.com/docs/react-dom" + }, + { + "value": "Apache-2.0", + "children.@humanwhocodes/config-array@npm:0.9.17.value.locator": "@humanwhocodes/config-array@npm:0.9.17", + "children.@humanwhocodes/config-array@npm:0.9.17.value.descriptor": "@humanwhocodes/config-array@npm:^0.9.8", + "children.@humanwhocodes/config-array@npm:0.9.17.children.url": "git+https://github.com/humanwhocodes/config-array.git", + "children.@humanwhocodes/config-array@npm:0.9.17.children.vendorName": "Nicholas C. Zakas", + "children.@humanwhocodes/config-array@npm:0.9.17.children.vendorUrl": "https://github.com/humanwhocodes/config-array#readme", + "children.typescript@patch:typescript@npm%3A4.3.7#~builtin::version=4.3.7&hash=c7623e.value.locator": "typescript@patch:typescript@npm%3A4.3.7#~builtin::version=4.3.7&hash=c7623e", + "children.typescript@patch:typescript@npm%3A4.3.7#~builtin::version=4.3.7&hash=c7623e.value.descriptor": "typescript@patch:typescript@4.3.7#~builtin", + "children.typescript@patch:typescript@npm%3A4.3.7#~builtin::version=4.3.7&hash=c7623e.children.url": "https://github.com/Microsoft/TypeScript.git", + "children.typescript@patch:typescript@npm%3A4.3.7#~builtin::version=4.3.7&hash=c7623e.children.vendorName": "Microsoft Corp.", + "children.typescript@patch:typescript@npm%3A4.3.7#~builtin::version=4.3.7&hash=c7623e.children.vendorUrl": "https://www.typescriptlang.org/" + }, + { + "value": "UNKNOWN", + "children.my-space@workspace:..value.locator": "my-space@workspace:.", + "children.my-space@workspace:..value.descriptor": "my-space@workspace:." + } +] diff --git a/documentation/files/yarnmodernlicenses.json b/documentation/files/yarnmodernlicenses.json new file mode 100644 index 00000000..3b1ac5dd --- /dev/null +++ b/documentation/files/yarnmodernlicenses.json @@ -0,0 +1,10 @@ +[ + { + "value": "MIT", + "children.@babel/runtime@npm:6.15.7.value.locator": "@babel/runtime@npm:6.15.7", + "children.@babel/runtime@npm:6.15.7.value.descriptor": "@babel/runtime@npm:^6.17.7", + "children.@babel/runtime@npm:6.15.7.children.url": "https://github.com/babel/babel.git", + "children.@babel/runtime@npm:6.15.7.children.vendorName": "The Babel Team", + "children.@babel/runtime@npm:6.15.7.children.vendorUrl": "https://babel.dev/docs/en/next/babel-runtime", + } +] diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 9d8f74ab..63563a0e 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -686,7 +686,7 @@ In _Solicitor_ the data is read with the following part of the config } ] ---- -=== Yarn +=== Yarn Classic (Yarn 1) To generate the input file required for Solicitor, yarn needs to be executed with the following command within the directory that contains the project's package.json (we require JSON output here): @@ -714,6 +714,37 @@ In _Solicitor_ the data is read with the following part of the config } ] ---- +=== Yarn Modern (Yarn 2 and above) + +In Yarn Modern the functionality to create a licenses report can be achieved with a separate component: https://github.com/mhassan1/yarn-plugin-licenses + +To generate the input file required for Solicitor, the plugin needs to be executed with the following command within the directory that contains the project's package.json (we require JSON output here): + +---- +yarn licenses list --production --recursive --json > /path/to/yarnmodernlicenses.json +---- + +The export should look like the following + +[source] +---- +include::files/yarnmodernlicenses.json[] + +---- + +Source: https://github.com/mhassan1/yarn-plugin-licenses + +In _Solicitor_ the data is read with the following part of the config + +---- +"readers" : [ { + "type" : "yarn-modern", + "source" : "file:path/to/yarnmodernlicenses.json", + "usagePattern" : "STATIC_LINKING" +} ] +---- + + === Pip To generate the input file required for Solicitor, one has to follow two steps: @@ -1671,6 +1702,7 @@ Changes in 1.20.0:: * https://github.com/devonfw/solicitor/issues/232: Set a standard for ordering LicenseNameMapping rules. Rules with an 'or-later' suffix are put before '-only' rules. * https://github.com/devonfw/solicitor/issues/234: Correct handling of new data model fields in ModelImporterExporter `dataStatus`,`traceabilityNotes` etc. * https://github.com/devonfw/solicitor/pull/235: Improvements in Curation Data Handling. When the curationDataSelector parameter is set to "none," no curations will be applied. +* https://github.com/devonfw/solicitor/issues/237: New Reader for license info of systems built with Yarn Modern. See <>. Changes in 1.19.0:: * https://github.com/devonfw/solicitor/issues/227: Fixed a bug where the `dataStatus` field in the aggregated OSS-Inventory was not filled. From c7200dccfe66b2406504433789a9e89e70962d18 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:49:27 +0100 Subject: [PATCH 106/139] Version 1.20.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index c2bdad33..41340f4a 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.20.0-SNAPSHOT + 1.20.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index dd6b4d20..a6773cdb 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.20.0-SNAPSHOT + 1.20.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 30b22cc9..d672505b 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.20.0-SNAPSHOT + 1.20.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index add38e89..90c44dd4 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.20.0-SNAPSHOT + 1.20.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index e843b85b..602e05f7 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.20.0-SNAPSHOT + 1.20.0 pom Solicitor Aggregator From 35cf767ee1e2f7167b1fab119a9743160c85dde8 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:11:02 +0100 Subject: [PATCH 107/139] Set version to SNAPSHOT of next minor release (1.21.0-SNAPSHOT) --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 41340f4a..25bebcb4 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.20.0 + 1.21.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index a6773cdb..aaf22d97 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.20.0 + 1.21.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 63563a0e..d52a448d 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1698,6 +1698,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.21.0:: + Changes in 1.20.0:: * https://github.com/devonfw/solicitor/issues/232: Set a standard for ordering LicenseNameMapping rules. Rules with an 'or-later' suffix are put before '-only' rules. * https://github.com/devonfw/solicitor/issues/234: Correct handling of new data model fields in ModelImporterExporter `dataStatus`,`traceabilityNotes` etc. diff --git a/documentation/pom.xml b/documentation/pom.xml index d672505b..79064104 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.20.0 + 1.21.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 90c44dd4..37336a38 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.20.0 + 1.21.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 602e05f7..71a49c62 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.20.0 + 1.21.0-SNAPSHOT pom Solicitor Aggregator From a38bf81d2975f735ecb301eb9c644fae8e26a7bf Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 15 Mar 2024 12:15:30 +0100 Subject: [PATCH 108/139] Avoid path traversal threats (#239) * Introduce and make use of IOHelper.securePath() to further harden against path traversal threats. * Limit DirectUrlWebContentProvider to only handle http and https URLs. * Update release notes * Rename method from securePath to secureFilePath to better clarify scope/purpose * fixed typo in comment Co-authored-by: chrimih <101123170+chrimih@users.noreply.github.com> --------- Co-authored-by: chrimih <101123170+chrimih@users.noreply.github.com> --- .../tools/solicitor/common/IOHelper.java | 44 +++++++++++- .../tools/solicitor/common/LogMessages.java | 3 +- .../FilesystemCachingContentProvider.java | 2 +- .../web/DirectUrlWebContentProvider.java | 21 +++++- .../FileScancodeRawComponentInfoProvider.java | 50 +++++++++----- .../scancode/MultilineHelper.java | 53 ++++++++++++++ .../tools/solicitor/common/IOHelperTest.java | 69 +++++++++++++++++++ .../web/DirectUrlWebContentProviderTest.java | 35 ++++++++++ ...redScancodeComponentInfoProviderTests.java | 5 +- .../ScancodeComponentInfoAdapterTest.java | 11 +-- documentation/master-solicitor.asciidoc | 1 + 11 files changed, 264 insertions(+), 30 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/MultilineHelper.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/common/IOHelperTest.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProviderTest.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/IOHelper.java b/core/src/main/java/com/devonfw/tools/solicitor/common/IOHelper.java index e1bb79c4..cd188014 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/IOHelper.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/IOHelper.java @@ -10,6 +10,8 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,7 +49,7 @@ public static String readStringFromInputStream(InputStream inp) throws IOExcepti /** * Assure that the directory in which the given file should be located exists. Try to create the directory if it does * not yet exist. - * + * * @param targetFilename the name (including path) of a file * @see #checkAndCreateLocation(File) */ @@ -59,7 +61,7 @@ public static void checkAndCreateLocation(String targetFilename) { /** * Assure that the directory in which the given file should be located exists. Try to create the directory if it does * not yet exist. - * + * * @param targetFile a file */ public static void checkAndCreateLocation(File targetFile) { @@ -82,6 +84,44 @@ public static void checkAndCreateLocation(File targetFile) { } + /** + * Create a file path from the given base path and all provided relative paths. The method assures that each relative + * path applied to the already existing (intermediate) path does not point to some place outside the existing path. + * + * @param basePath the base file path + * @param relativePaths any number of relative file paths to be appended + * @return a file path built from the given arguments + * @throws IllegalArgumentException if any of the relativePaths points to some point outside the already existing path + * or if any of the relativePaths are absolute paths. + * @throws NullPointerException if the basePath or any of the relativePaths are null + */ + public static String secureFilePath(String basePath, String... relativePaths) { + + if (basePath == null) { + throw new NullPointerException("Base path must not be null"); + } + Path path = Paths.get(basePath).normalize(); + + for (String relative : relativePaths) { + if (relative == null) { + throw new NullPointerException("Relative paths must not be null"); + } + + Path relativePath = Paths.get(relative); + if (relativePath.isAbsolute()) { + throw new IllegalArgumentException( + "Given path element '" + relative + "' is absolute but only relative paths are allowed"); + } + Path newPath = path.resolve(relativePath).normalize(); + if (!newPath.startsWith(path)) { + throw new IllegalArgumentException( + "Given path element '" + relative + "' would result in escaping the parent tree hierarchy and is rejected"); + } + path = newPath; + } + return path.toString(); + } + /** * Private constructor which prevents instantiation */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 7c7a112e..311232ac 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -103,7 +103,8 @@ public enum LogMessages { MODERN_YARN_VIRTUAL_PACKAGE(69, "When reading yarn license info from file '{}' there was at least one virtual package encountered. Check if package resolution is correct"), // MODERN_YARN_PATCHED_PACKAGE(70, - "When reading yarn license info from file '{}' there was at least one patched package encountered. Processing only the base package, not the patched version."); + "When reading yarn license info from file '{}' there was at least one patched package encountered. Processing only the base package, not the patched version."), // + FAILED_READING_FILE(71, "Reading file '{}' failed"); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/content/FilesystemCachingContentProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/common/content/FilesystemCachingContentProvider.java index f1b1ee9b..6f6052e3 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/content/FilesystemCachingContentProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/content/FilesystemCachingContentProvider.java @@ -67,7 +67,7 @@ public C loadFromNext(String url) { if (!url.startsWith("file:")) { // data of URLs which resolve to local file will not be cached - File file = new File(this.resourceDirectory + "/" + getKey(url)); + File file = new File(IOHelper.secureFilePath(this.resourceDirectory, getKey(url))); File targetDir = file.getParentFile(); try { IOHelper.checkAndCreateLocation(file); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProvider.java index c8c075c0..59812f09 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProvider.java @@ -26,6 +26,9 @@ public class DirectUrlWebContentProvider implements ContentProvider private boolean skipdownload; + private static final Pattern SUPPORTED_URL_PATTERNS = Pattern.compile("^http:.*|^https:.*|^jar:http:.*|^jar:https:.*", + Pattern.CASE_INSENSITIVE); + /** * Constructor. * @@ -40,7 +43,8 @@ public DirectUrlWebContentProvider(boolean skipdownload) { /** * {@inheritDoc} * - * Directly tries to access the given URL via the web. + * Directly tries to access the given URL via the web. Only URLs matching {@link #SUPPORTED_URL_PATTERNS} will be + * processed. All others return an empty WebContent. */ @Override public WebContent getContentForUri(String url) { @@ -49,6 +53,10 @@ public WebContent getContentForUri(String url) { if (url == null) { return new WebContent(null); } + if (!isSupportedUrl(url)) { + LOG.debug("Accessing URL '{}' is not supported by DirectUrlWebContentProvider, returning empty WebContent", url); + return new WebContent(null); + } if (this.skipdownload) { LOG.info(LogMessages.SKIP_DOWNLOAD.msg(), url); return new WebContent(null); @@ -84,6 +92,17 @@ public WebContent getContentForUri(String url) { return new WebContent(null); } + /** + * Indicates if the given URL is supported by this {@link ContentProvider}. + * + * @param url the url to check + * @return true if url is supported, false otherwise. + */ + boolean isSupportedUrl(String url) { + + return SUPPORTED_URL_PATTERNS.matcher(url).matches(); + } + /** * @param input * @param lineInfo diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java index 990d45bb..c4397c60 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java @@ -4,6 +4,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Scanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,7 +13,7 @@ import org.springframework.stereotype.Component; import com.devonfw.tools.solicitor.common.IOHelper; -import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; +import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; import com.fasterxml.jackson.databind.JsonNode; @@ -39,19 +40,14 @@ public class FileScancodeRawComponentInfoProvider implements ScancodeRawComponen private AllKindsPackageURLHandler packageURLHandler; - private DirectUrlWebContentProvider contentProvider; - /** * The constructor. * - * @param contentProvider content provider for accessing source files of the packag * @param packageURLHandler handler to deal with PackageURLs. */ @Autowired - public FileScancodeRawComponentInfoProvider(DirectUrlWebContentProvider contentProvider, - AllKindsPackageURLHandler packageURLHandler) { + public FileScancodeRawComponentInfoProvider(AllKindsPackageURLHandler packageURLHandler) { - this.contentProvider = contentProvider; this.packageURLHandler = packageURLHandler; } @@ -81,7 +77,7 @@ public ScancodeRawComponentInfo readScancodeData(String packageUrl) throws ComponentInfoAdapterException, ScancodeProcessingFailedException { String packagePathPart = this.packageURLHandler.pathFor(packageUrl); - String path = this.repoBasePath + "/" + packagePathPart + "/scancode.json"; + String path = IOHelper.secureFilePath(this.repoBasePath, packagePathPart, "scancode.json"); File scanCodeFile = new File(path); if (!scanCodeFile.exists()) { @@ -114,13 +110,13 @@ private void throwExceptionForDownloadOrScanningFailures(String packagePathPart) throws ScancodeProcessingFailedException { // Check if "sources.failed" exists - String sourcesFailedPath = this.repoBasePath + "/" + packagePathPart + "/sources.failed"; + String sourcesFailedPath = IOHelper.secureFilePath(this.repoBasePath, packagePathPart, "sources.failed"); File sourcesFailedFile = new File(sourcesFailedPath); if (sourcesFailedFile.exists()) { throw new ScancodeProcessingFailedException("Downloading of package sources had failed."); } // Check if "scancodeScan.failed" exists - String scancodeScanFailedPath = this.repoBasePath + "/" + packagePathPart + "/scancodeScan.failed"; + String scancodeScanFailedPath = IOHelper.secureFilePath(this.repoBasePath, packagePathPart, "scancodeScan.failed"); File scancodeScanFailedFile = new File(scancodeScanFailedPath); if (scancodeScanFailedFile.exists()) { throw new ScancodeProcessingFailedException("Scanning of package sources had failed."); @@ -138,7 +134,7 @@ private void addOriginData(String packageUrl, ScancodeRawComponentInfo component throws ComponentInfoAdapterException { String packagePathPart = this.packageURLHandler.pathFor(packageUrl); - String path = this.repoBasePath + "/" + packagePathPart + "/origin.yaml"; + String path = IOHelper.secureFilePath(this.repoBasePath, packagePathPart, "origin.yaml"); File originFile = new File(path); if (!originFile.exists()) { @@ -174,17 +170,39 @@ private void addOriginData(String packageUrl, ScancodeRawComponentInfo component public String retrieveContent(String packageUrl, String fileUri) { if (!fileUri.startsWith(PKG_CONTENT_SCHEMA_PREFIX)) { + // we only handle pkgcontent: URIs here! return null; } if (fileUri.contains("..")) { - // prevent directory traversal + // prevent directory traversal (there are other measures to also prevent this, but lets do it here explicitly) LOG.debug("Suspicious file traversal in URI '{}', returning null", fileUri); return null; } - String pathWithoutPrefix = fileUri.substring(PKG_CONTENT_SCHEMA_PREFIX.length()); - String directUrl = "file:" + this.repoBasePath + "/" + this.packageURLHandler.pathFor(packageUrl) + "/" - + SOURCES_DIR + pathWithoutPrefix; - return this.contentProvider.getContentForUri(directUrl).getContent(); + String pkgContentUriWithoutPrefix = fileUri.substring(PKG_CONTENT_SCHEMA_PREFIX.length()); + + String relativeFilePathAndName = pkgContentUriWithoutPrefix; + String lineInfo = null; + int startOfLineInfo = pkgContentUriWithoutPrefix.indexOf("#L"); + if (startOfLineInfo >= 0) { + lineInfo = pkgContentUriWithoutPrefix.substring(startOfLineInfo, pkgContentUriWithoutPrefix.length()); + relativeFilePathAndName = pkgContentUriWithoutPrefix.substring(0, startOfLineInfo); + } + + String fullFilePathAndName = IOHelper.secureFilePath(this.repoBasePath, this.packageURLHandler.pathFor(packageUrl), + SOURCES_DIR, relativeFilePathAndName); + File file = new File(fullFilePathAndName); + try (InputStream is = new FileInputStream(file); Scanner s = new Scanner(is)) { + s.useDelimiter("\\A"); + String result = s.hasNext() ? s.next() : ""; + + return MultilineHelper.possiblyExtractLines(result, lineInfo); + } catch (IOException e) { + if (LOG.isDebugEnabled()) { + LOG.debug("Could not retrieve content from file '" + fullFilePathAndName + "'", e); + } + LOG.info(LogMessages.FAILED_READING_FILE.msg(), fullFilePathAndName, e.getClass().getSimpleName()); + } + return null; } @Override diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/MultilineHelper.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/MultilineHelper.java new file mode 100644 index 00000000..60a29ee9 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/MultilineHelper.java @@ -0,0 +1,53 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A helper class which supports extracting a range of lines from a given (multiline) string. + */ +public class MultilineHelper { + + /** + * Constructor. Prevents instantiation. + * + */ + private MultilineHelper() { + + } + + /** + * Extracts a range of lines from the given (multiline) input. + * + * @param input the multiline input + * @param lineInfo lines to extract, given as #L17-L20. null indicates that the whole input + * should be returned. + * @return the extracted lines. + */ + public static String possiblyExtractLines(String input, String lineInfo) { + + if (lineInfo == null) { + return input; + } + Pattern pattern = Pattern.compile("#L(\\d+)(-L(\\d+))?"); + Matcher matcher = pattern.matcher(lineInfo); + if (matcher.find()) { + int startLine = Integer.parseInt(matcher.group(1)); + int endLine = Integer.parseInt(matcher.group(3) != null ? matcher.group(3) : matcher.group(1)); + String[] splitted = input.split("\\n"); + StringBuffer result = new StringBuffer(); + for (int i = 0; i < splitted.length; i++) { + if (i + 1 >= startLine && i + 1 <= endLine) { + result.append(splitted[i]).append("\n"); + } + } + return result.toString(); + } else { + throw new IllegalStateException("Regex did not find line info - this seems to be a bug."); + } + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/common/IOHelperTest.java b/core/src/test/java/com/devonfw/tools/solicitor/common/IOHelperTest.java new file mode 100644 index 00000000..7010c5be --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/common/IOHelperTest.java @@ -0,0 +1,69 @@ +package com.devonfw.tools.solicitor.common; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.File; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link IOHelper}. + * + */ +class IOHelperTest { + + /** + * @throws java.lang.Exception + */ + @BeforeEach + void setUp() throws Exception { + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.common.IOHelper#secureFilePath(java.lang.String, java.lang.String[])}. + */ + @Test + void testSecureFilePath() { + + assertEquals(fixSep("base"), IOHelper.secureFilePath("base")); + assertEquals(fixSep("base/r1"), IOHelper.secureFilePath("base", "r1")); + assertEquals(fixSep("base/r1/r2"), IOHelper.secureFilePath("base", "r1", "r2")); + assertEquals(fixSep("base/r1/r2"), IOHelper.secureFilePath("base", "r1///", "r2/././")); + assertEquals(fixSep("/base/r1/r2"), IOHelper.secureFilePath("/base", "r1///", "r2/././")); + + assertThrows(IllegalArgumentException.class, () -> { + IOHelper.secureFilePath("base", "/r1", "r2"); + }); + + assertThrows(IllegalArgumentException.class, () -> { + IOHelper.secureFilePath("base", "../r1", "r2"); + }); + + assertThrows(IllegalArgumentException.class, () -> { + IOHelper.secureFilePath("base", "r1", "../r2"); + }); + + assertEquals(fixSep("base/r1/r2"), IOHelper.secureFilePath("base", "a/../r1", "r2")); + + assertThrows(IllegalArgumentException.class, () -> { + IOHelper.secureFilePath("base", "a/../../r1", "r2"); + }); + } + + /** + * Returns the given strings with all occurrences of / or \\ to be replaced by the system + * dependent file separator character. This is required to handle differences between Windows and Unix. + * + * @param input the origin string + * @return the fixed string + */ + private static String fixSep(String input) { + + return input.replace('/', File.separatorChar).replace('\\', File.separatorChar); + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProviderTest.java b/core/src/test/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProviderTest.java new file mode 100644 index 00000000..72b42c1a --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/common/content/web/DirectUrlWebContentProviderTest.java @@ -0,0 +1,35 @@ +package com.devonfw.tools.solicitor.common.content.web; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link DirectUrlWebContentProvider} + * + */ +class DirectUrlWebContentProviderTest { + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider#isSupportedUrl(java.lang.String)}. + */ + @Test + void testIsSupportedUrl() { + + DirectUrlWebContentProvider provider = new DirectUrlWebContentProvider(false); + + assertTrue(provider.isSupportedUrl("http:foo")); + assertTrue(provider.isSupportedUrl("https:foo")); + assertTrue(provider.isSupportedUrl("jar:http:foo")); + assertTrue(provider.isSupportedUrl("jar:https:foo")); + + assertTrue(provider.isSupportedUrl("hTTp:foo")); + + assertFalse(provider.isSupportedUrl("file:some.file")); + assertFalse(provider.isSupportedUrl("file:http:foo")); + + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java index 0ee9d141..909a37cb 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java @@ -7,7 +7,6 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; @@ -32,10 +31,8 @@ public void setup() { Mockito.when(packageURLHandler.pathFor("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0")) .thenReturn("pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0"); - DirectUrlWebContentProvider contentProvider = new DirectUrlWebContentProvider(false); - this.fileScancodeRawComponentInfoProvider = new FileScancodeRawComponentInfoProvider(contentProvider, - packageURLHandler); + this.fileScancodeRawComponentInfoProvider = new FileScancodeRawComponentInfoProvider(packageURLHandler); this.fileScancodeRawComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); this.singleFileCurationProvider = new SingleFileCurationProvider(packageURLHandler); diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java index 6d62b088..789528a4 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java @@ -13,7 +13,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import com.devonfw.tools.solicitor.common.content.web.DirectUrlWebContentProvider; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; @@ -46,10 +45,11 @@ public void setup() { Mockito.when(packageURLHandler.pathFor("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0")) .thenReturn("pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0"); - DirectUrlWebContentProvider contentProvider = new DirectUrlWebContentProvider(false); - this.fileScancodeRawComponentInfoProvider = new FileScancodeRawComponentInfoProvider(contentProvider, - packageURLHandler); + Mockito.when(packageURLHandler.pathFor("pkg:maven/com.devonfw.tools/unknown@0.1.0")) + .thenReturn("pkg/maven/com/devonfw/tools/unknown/0.1.0"); + + this.fileScancodeRawComponentInfoProvider = new FileScancodeRawComponentInfoProvider(packageURLHandler); this.fileScancodeRawComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); this.singleFileCurationProvider = new SingleFileCurationProvider(packageURLHandler); @@ -70,7 +70,8 @@ public void setup() { } /** - * Test the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} method when such package is known. + * Test the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} method when such package is not + * known. * * @throws ComponentInfoAdapterException if something goes wrong */ diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index d52a448d..aadad3ff 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1699,6 +1699,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.21.0:: +* https://github.com/devonfw/solicitor/pull/239: Improving some internal components to reduce risk of path traversal attacks in case that these components are (re)used in some webservice implementation. Changes in 1.20.0:: * https://github.com/devonfw/solicitor/issues/232: Set a standard for ordering LicenseNameMapping rules. Rules with an 'or-later' suffix are put before '-only' rules. From 372ee37285a9322bb24951f431e028444e4a086b Mon Sep 17 00:00:00 2001 From: duph97 Date: Fri, 22 Mar 2024 18:04:26 +0100 Subject: [PATCH 109/139] Enhancement/attribution template (#241) * Add modified SQL files for FOSS_SBOM.xlsx and new Attributions.html * Modify Attributions format * Add release note * Fix condition * Fix where condition * Resolve merge conflict * Squashed commit of the following: commit a38bf81d2975f735ecb301eb9c644fae8e26a7bf Author: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri Mar 15 12:15:30 2024 +0100 Avoid path traversal threats (#239) * Introduce and make use of IOHelper.securePath() to further harden against path traversal threats. * Limit DirectUrlWebContentProvider to only handle http and https URLs. * Update release notes * Rename method from securePath to secureFilePath to better clarify scope/purpose * fixed typo in comment Co-authored-by: chrimih <101123170+chrimih@users.noreply.github.com> --------- Co-authored-by: chrimih <101123170+chrimih@users.noreply.github.com> commit 35cf767ee1e2f7167b1fab119a9743160c85dde8 Author: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon Mar 11 12:11:02 2024 +0100 Set version to SNAPSHOT of next minor release (1.21.0-SNAPSHOT) commit c7200dccfe66b2406504433789a9e89e70962d18 Author: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon Mar 11 11:49:27 2024 +0100 Version 1.20.0 commit fed2f9dfc46dc2124d87ec92281354bb905a6fab Author: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon Mar 11 11:15:45 2024 +0100 Implementation of YarnModernReader to support Yarn 2 and above (#238) # Conflicts: # documentation/master-solicitor.asciidoc * Add release note (after merge conflict resolved) * remove ossHomepage from result as it is not needed and might result in duplicates (if e.g. data is read in by different reader types where only one fills the attribute, like gradle2/maven) --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- ...ncomponents_with_noncommerciallicenses.sql | 16 +++++---- ...th_noncommerciallicenses_with_licenses.sql | 6 ++-- .../tools/solicitor/sql/noticefiles.sql | 1 + .../sql/ossapplicationcomponents.sql | 6 ++-- ...uelicenses_with_application_components.sql | 1 + .../tools/solicitor/templates/Attributions.vm | 33 ++++++++++++------- documentation/master-solicitor.asciidoc | 3 +- 7 files changed, 43 insertions(+), 23 deletions(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql index 4afb4618..4ff98d50 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses.sql @@ -5,9 +5,13 @@ select GROUP_CONCAT(DISTINCT ac."version" ORDER BY "version" DESC SEPARATOR ', ') as "version" , ac."groupId", ac."artifactId", - ac."ossHomepage", + ac."packageUrl", ac."sourceRepoUrl", - ac."copyrights" + ac."sourceDownloadUrl", + ac."packageDownloadUrl", + ac."copyrights", + GROUP_CONCAT(DISTINCT CASE WHEN l."effectiveNormalizedLicenseType" = 'IGNORE' THEN CONCAT(l."normalizedLicense", ' (NA)') WHEN l."effectiveNormalizedLicense" != l."normalizedLicense" THEN CONCAT(l."normalizedLicense", ' (redistributed under ', l."effectiveNormalizedLicense", ')') ELSE l."normalizedLicense" END ORDER BY "normalizedLicense" DESC SEPARATOR ', ') as "licenses" + from APPLICATION a, APPLICATIONCOMPONENT ac, @@ -19,10 +23,10 @@ where group by "groupId", "artifactId", - "ossHomepage", + "packageUrl", "sourceRepoUrl", + "sourceDownloadUrl", + "packageDownloadUrl", "copyrights" order by - "groupId", - "artifactId", - "version" + "packageUrl" diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql index 3230f55c..36df5b73 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/applicationcomponents_with_noncommerciallicenses_with_licenses.sql @@ -5,8 +5,8 @@ select GROUP_CONCAT(DISTINCT ac."version" ORDER BY "version" DESC SEPARATOR ', ') as "version" , ac."groupId", ac."artifactId", - ac."ossHomepage", - ac."sourceRepoUrl", + ac."packageUrl", + ac."sourceRepoUrl", ac."copyrights", l."normalizedLicense", l."effectiveNormalizedLicense" @@ -21,7 +21,7 @@ where group by "groupId", "artifactId", - "ossHomepage", + "packageUrl", "sourceRepoUrl", "copyrights", "normalizedLicense", diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql index 83e09d89..9ada6f53 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/noticefiles.sql @@ -3,6 +3,7 @@ -- returns all distinct notice file texts select distinct GROUP_CONCAT(DISTINCT CONCAT( ac."artifactId", ' (', ac."version", ')' ) ORDER BY "artifactId" ASC, "version" ASC SEPARATOR ', ') as "artifact", + GROUP_CONCAT(DISTINCT ac."packageUrl" ORDER BY "packageUrl" ASC SEPARATOR ', ') as "packageUrl", ARRAY_AGG(DISTINCT ac."noticeFileContent" ORDER BY "noticeFileContent" DESC)[1] as "noticeFileContent", UCASE(REGEXP_REPLACE(ac."noticeFileContent",'\s','')) as "unifiedNoticeFileContent" from diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql index b9ff9f89..18b1f3f1 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql @@ -7,7 +7,8 @@ select ac."artifactId", ac."packageUrl", ac."copyrights", - l."effectiveNormalizedLicense", + l."effectiveNormalizedLicense", + l."effectiveNormalizedLicenseType", l."effectiveNormalizedLicenseUrl", l."effectiveNormalizedLicenseContent", UCASE(REGEXP_REPLACE(l."effectiveNormalizedLicenseContent",'\s','')) as "unifiedEffectiveNormalizedLicenseContent" @@ -18,13 +19,14 @@ from where a.ID_APPLICATION = ac.PARENT_APPLICATIONCOMPONENT AND ac.ID_APPLICATIONCOMPONENT = l.PARENT_NORMALIZEDLICENSE AND - l."effectiveNormalizedLicenseType" like 'OSS-%' + (l."effectiveNormalizedLicenseType" LIKE 'OSS-%' OR l."effectiveNormalizedLicenseType" = 'SCANCODE') group by "groupId", "artifactId", "packageUrl", "copyrights", "effectiveNormalizedLicense", + "effectiveNormalizedLicenseType", "effectiveNormalizedLicenseUrl", "effectiveNormalizedLicenseContent", "unifiedEffectiveNormalizedLicenseContent" diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql index 61c91d4b..3a0d150e 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/uniquelicenses_with_application_components.sql @@ -4,6 +4,7 @@ select distinct GROUP_CONCAT(DISTINCT l."normalizedLicense" ORDER BY "normalizedLicense" ASC SEPARATOR ', ') as "normalizedLicense", GROUP_CONCAT(DISTINCT CONCAT( ac."artifactId", ' (', ac."version", ')' ) ORDER BY "artifactId" ASC, "version" ASC SEPARATOR ', ') as "artifact", + GROUP_CONCAT(DISTINCT ac."packageUrl" ORDER BY "packageUrl" ASC SEPARATOR ', ') as "packageUrl", ARRAY_AGG(DISTINCT l."normalizedLicenseContent" ORDER BY "normalizedLicenseContent" DESC)[1] as "normalizedLicenseContent", UCASE(REGEXP_REPLACE(l."normalizedLicenseContent",'\s','')) as "unifiedNormalizedLicenseContent" from diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm index dfd07f9b..562ee775 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm +++ b/core/src/main/resources/com/devonfw/tools/solicitor/templates/Attributions.vm @@ -47,9 +47,9 @@ This document has 3 sections:

        Component / License overview

        -The following table lists all third party Open Source Components that may be contained in this product. Besides name and version the table lists: +The following table lists all third party Open Source Components that may be contained in this product. Besides the package URL (identifier of the componente) the table lists:
          -
        • the URL of the Source-Code-Repository for downloading the source code
        • +
        • the URL of the Source-Code-Repository or Source-Code archive for downloading the source code
        • the names of the OSS licenses attached to the component; in case that the component is dual-/multilicensed (NA) indicates that the component is herein distributed under an alternative license and the license does not apply in the context of this distribution
        • copyright information given in the third party component; in case that no direct copyright notices are given then author/contributor information might be given if available
        @@ -58,8 +58,7 @@ The following table lists all third party Open Source Components that may be con ## head row - - + @@ -72,13 +71,21 @@ The following table lists all third party Open Source Components that may be con #set( $aid = $license.artifactId ) #set( $gid = $license.groupId ) #set( $ver = $license.version ) + #set( $purl = $license.packageUrl ) #set( $apps = $license.APPS ) - ## application component name - ## application component version - +## ## application component name +## ## application component version + ## application component package Url + - ## copyrights + #if( "$!license.copyrights" != "") + ## copyrights + #else + ## no copyrights given + #end #end @@ -130,7 +141,7 @@ options.

        Defined/referenced license(s): $ul.normalizedLicense
        -Applicable component(s): $ul.artifact +Applicable component(s): $ul.packageUrl

        #end
        @@ -142,7 +153,7 @@ The following additional attribution notices are given within NOTICE files and a #foreach($nf in $NOTICEFILES)

        -Component(s): $nf.artifact +Component(s): $nf.packageUrl

        #if ($nf.noticeFileContent) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index aadad3ff..7290b5ee 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1700,6 +1700,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.21.0:: * https://github.com/devonfw/solicitor/pull/239: Improving some internal components to reduce risk of path traversal attacks in case that these components are (re)used in some webservice implementation. +* https://github.com/devonfw/solicitor/issues/240: Improve Attributions.html to use packageUrl. Fixed bug where license texts for components with license type 'SCANCODE' were not printed. Changes in 1.20.0:: * https://github.com/devonfw/solicitor/issues/232: Set a standard for ordering LicenseNameMapping rules. Rules with an 'or-later' suffix are put before '-only' rules. @@ -1873,4 +1874,4 @@ Changes in 1.0.7:: * https://github.com/devonfw/solicitor/issues/56: Enable continuing analysis in multiapplication projects even is some license files are unavailable. * Described simplified usage of license-maven-plugin without need to change pom.xml. (Documentation only) -* Ensure consistent sorting even in case that multiple "Ignored" licenses exist for a component +* Ensure consistent sorting even in case that multiple "Ignored" licenses exist for a component \ No newline at end of file From d79d974119f62817b19805024a6b057888038fe5 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:23:58 +0100 Subject: [PATCH 110/139] Version 1.21.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 25bebcb4..61271303 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.21.0-SNAPSHOT + 1.21.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index aaf22d97..6f33ac0e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.21.0-SNAPSHOT + 1.21.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 79064104..3f376985 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.21.0-SNAPSHOT + 1.21.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 37336a38..60e87507 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.21.0-SNAPSHOT + 1.21.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 71a49c62..d73d8939 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.21.0-SNAPSHOT + 1.21.0 pom Solicitor Aggregator From 93f23d8d95ff89d27c0baa5b1606db029e8d7499 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:29:46 +0100 Subject: [PATCH 111/139] Set version to SNAPSHOT of next minor release (1.22.0-SNAPSHOT) --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 61271303..40c2a208 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.21.0 + 1.22.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 6f33ac0e..46077233 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.21.0 + 1.22.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 7290b5ee..760cdbb7 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1698,6 +1698,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.22.0:: + Changes in 1.21.0:: * https://github.com/devonfw/solicitor/pull/239: Improving some internal components to reduce risk of path traversal attacks in case that these components are (re)used in some webservice implementation. * https://github.com/devonfw/solicitor/issues/240: Improve Attributions.html to use packageUrl. Fixed bug where license texts for components with license type 'SCANCODE' were not printed. diff --git a/documentation/pom.xml b/documentation/pom.xml index 3f376985..2e286130 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.21.0 + 1.22.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 60e87507..59e2196b 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.21.0 + 1.22.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index d73d8939..0c155e80 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.21.0 + 1.22.0-SNAPSHOT pom Solicitor Aggregator From 9ea77ee6211003f569e262d9e231eea0298626d5 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:31:32 +0100 Subject: [PATCH 112/139] Protect MavenReader against XXE vulnerability (#243) * protect MavenReader against XXE vulnerability * Update release notes. * Add XXE to the spellcheck dictionary --- .../solicitor/reader/maven/MavenReader.java | 31 +++++++++++++++-- .../reader/maven/MavenReaderTests.java | 33 +++++++++++++++++++ .../licenses_sample_with_doctype.xml | 17 ++++++++++ documentation/master-solicitor.asciidoc | 1 + solicitor.dict | 1 + 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 core/src/test/resources/licenses_sample_with_doctype.xml diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/maven/MavenReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/maven/MavenReader.java index 1eda8f75..ee48d2d7 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/maven/MavenReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/maven/MavenReader.java @@ -10,11 +10,20 @@ import java.util.Map; import java.util.Set; +import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Source; +import javax.xml.transform.sax.SAXSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import com.devonfw.tools.solicitor.common.PackageURLHelper; import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; @@ -33,6 +42,7 @@ */ @Component public class MavenReader extends AbstractReader implements Reader { + private static final Logger LOG = LoggerFactory.getLogger(MavenReader.class); /** * The supported type of this {@link Reader}. @@ -63,11 +73,26 @@ public void readInventory(String type, String sourceUrl, Application application JAXBContext jaxbContext; try { + SAXParserFactory secureParserFactory = SAXParserFactory.newInstance(); + secureParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + secureParserFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + secureParserFactory.setXIncludeAware(false); + + Source xmlSource = new SAXSource(secureParserFactory.newSAXParser().getXMLReader(), new InputSource(is)); + jaxbContext = JAXBContext.newInstance(LicenseSummary.class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); - ls = (LicenseSummary) unmarshaller.unmarshal(is); - } catch (JAXBException e) { - throw new SolicitorRuntimeException("Could nor read maven license info", e); + ls = (LicenseSummary) unmarshaller.unmarshal(xmlSource); + } catch (JAXBException | SAXException | ParserConfigurationException e) { + throw new SolicitorRuntimeException("Could not read maven license info", e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + LOG.debug("Exception while attemping to close inputs stream for reading maven license data", e); + } + } } for (Dependency dep : ls.getDependencies()) { diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/maven/MavenReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/maven/MavenReaderTests.java index 147d6179..13f492c1 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/maven/MavenReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/maven/MavenReaderTests.java @@ -6,12 +6,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import javax.xml.bind.UnmarshalException; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.devonfw.tools.solicitor.common.FileInputStreamFactory; +import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; import com.devonfw.tools.solicitor.model.ModelFactory; import com.devonfw.tools.solicitor.model.impl.ModelFactoryImpl; import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; @@ -21,6 +25,9 @@ public class MavenReaderTests { private static final Logger LOG = LoggerFactory.getLogger(MavenReaderTests.class); + /** + * Tests reading a maven license file. + */ @Test public void readFileAndCheckSize() { @@ -50,4 +57,30 @@ public void readFileAndCheckSize() { assertTrue(found); } + + /** + * Tests if the MavenReader rejects XML with DOCTYPE declaration which is done to prevent XXE. + */ + @Test + public void testProtectionAgainstXxe() { + + ModelFactory modelFactory = new ModelFactoryImpl(); + + Application application = modelFactory.newApplication("testApp", "0.0.0.TEST", "1.1.2111", "http://bla.com", + "Java8"); + MavenReader mr = new MavenReader(); + mr.setModelFactory(modelFactory); + mr.setInputStreamFactory(new FileInputStreamFactory()); + + try { + mr.readInventory("maven", "src/test/resources/licenses_sample_with_doctype.xml", application, + UsagePattern.DYNAMIC_LINKING, "maven", null); + fail("Expected exception was not thrown"); + } catch (SolicitorRuntimeException e) { + // we check detailed message to make sure the exception is not thrown due to other reasons + UnmarshalException ume = (UnmarshalException) (e.getCause()); + assertTrue(ume.getLinkedException().getMessage().contains("DOCTYPE is disallowed")); + } + + } } diff --git a/core/src/test/resources/licenses_sample_with_doctype.xml b/core/src/test/resources/licenses_sample_with_doctype.xml new file mode 100644 index 00000000..41b0dfd3 --- /dev/null +++ b/core/src/test/resources/licenses_sample_with_doctype.xml @@ -0,0 +1,17 @@ + +]> + + + + org.apache.poi + poi-ooxml + 3.17 + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 760cdbb7..e6f6b10f 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1699,6 +1699,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.22.0:: +* https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. Changes in 1.21.0:: * https://github.com/devonfw/solicitor/pull/239: Improving some internal components to reduce risk of path traversal attacks in case that these components are (re)used in some webservice implementation. diff --git a/solicitor.dict b/solicitor.dict index acbc7e6c..1fbf192f 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -40,6 +40,7 @@ velo venv vm WTFPL +XXE zA Zlib Zope \ No newline at end of file From 0fc50000994e447b85e93e27b59641d5df6d2434 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 28 Mar 2024 23:43:32 +0100 Subject: [PATCH 113/139] Update Drools to 8.44.0.Final (#244) * Update Drools to 8.44.0.Final * Updated release notes --- core/pom.xml | 6 +++--- .../ruleengine/drools/DroolsDecisionTableReader.java | 2 +- documentation/master-solicitor.asciidoc | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 46077233..b667b794 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -73,17 +73,17 @@ org.kie kie-ci - 8.16.1.Beta + 8.44.0.Final org.drools drools-decisiontables - 8.16.1.Beta + 8.44.0.Final org.drools drools-xml-support - 8.16.1.Beta + 8.44.0.Final org.apache.poi diff --git a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsDecisionTableReader.java b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsDecisionTableReader.java index 3ce496be..738a1d72 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsDecisionTableReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsDecisionTableReader.java @@ -8,7 +8,7 @@ import java.util.Collection; import java.util.UUID; -import org.drools.core.builder.conf.impl.DecisionTableConfigurationImpl; +import org.drools.compiler.builder.conf.DecisionTableConfigurationImpl; import org.kie.api.builder.model.KieBaseModel; import org.kie.api.io.Resource; import org.kie.api.io.ResourceType; diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index e6f6b10f..099c11c1 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1699,7 +1699,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.22.0:: -* https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. +* https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. +* https://github.com/devonfw/solicitor/pull/244: Updated Drools rule engine to 8.44.0.Final. Changes in 1.21.0:: * https://github.com/devonfw/solicitor/pull/239: Improving some internal components to reduce risk of path traversal attacks in case that these components are (re)used in some webservice implementation. From 708d4e42a61db1abc2d07ca28abcb7eecf41d565 Mon Sep 17 00:00:00 2001 From: ohecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 29 Mar 2024 00:14:58 +0100 Subject: [PATCH 114/139] added license texts required for attribution document --- .../http___opensource_org_licenses_MIT | 7 + .../http___www_eclipse_org_legal_epl_2_0_ | 277 ++++++++++ ...gnu_org_licenses_old_licenses_lgpl_2_1_txt | 502 ++++++++++++++++++ 3 files changed, 786 insertions(+) create mode 100644 core/src/test/resources/licenses/http___opensource_org_licenses_MIT create mode 100644 core/src/test/resources/licenses/http___www_eclipse_org_legal_epl_2_0_ create mode 100644 core/src/test/resources/licenses/http___www_gnu_org_licenses_old_licenses_lgpl_2_1_txt diff --git a/core/src/test/resources/licenses/http___opensource_org_licenses_MIT b/core/src/test/resources/licenses/http___opensource_org_licenses_MIT new file mode 100644 index 00000000..05574e51 --- /dev/null +++ b/core/src/test/resources/licenses/http___opensource_org_licenses_MIT @@ -0,0 +1,7 @@ +Copyright + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/core/src/test/resources/licenses/http___www_eclipse_org_legal_epl_2_0_ b/core/src/test/resources/licenses/http___www_eclipse_org_legal_epl_2_0_ new file mode 100644 index 00000000..e23ece2c --- /dev/null +++ b/core/src/test/resources/licenses/http___www_eclipse_org_legal_epl_2_0_ @@ -0,0 +1,277 @@ +Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + +"Contributor" means any person or entity that Distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which +are necessarily infringed by the use or sale of its Contribution alone +or when combined with the Program. + +"Program" means the Contributions Distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement +or any Secondary License (as applicable), including Contributors. + +"Derivative Works" shall mean any work, whether in Source Code or other +form, that is based on (or derived from) the Program and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. + +"Modified Works" shall mean any work in Source Code or other form that +results from an addition to, deletion from, or modification of the +contents of the Program, including, for purposes of clarity any new file +in Source Code form that contains any contents of the Program. Modified +Works shall not include works that contain only declarations, +interfaces, types, classes, structures, or files of the Program solely +in each case in order to link to, bind by name, or subclass the Program +or Modified Works thereof. + +"Distribute" means the acts of a) distributing or b) making available +in any manner that enables the transfer of a copy. + +"Source Code" means the form of a Program preferred for making +modifications, including but not limited to software source code, +documentation source, and configuration files. + +"Secondary License" means either the GNU General Public License, +Version 2.0, or any later versions of that license, including any +exceptions or additional permissions as identified by the initial +Contributor. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + +3. REQUIREMENTS + +3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + +3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + +3.3 Contributors may not remove or alter any copyright, patent, +trademark, attribution notices, disclaimers of warranty, or limitations +of liability ("notices") contained within the Program from any copy of +the Program which they Distribute, provided that Contributors may add +their own appropriate notices. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities +with respect to end users, business partners and the like. While this +license is intended to facilitate the commercial use of the Program, +the Contributor who includes the Program in a commercial product +offering should do so in a manner which does not create potential +liability for other Contributors. Therefore, if a Contributor includes +the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and indemnify every +other Contributor ("Indemnified Contributor") against any losses, +damages and costs (collectively "Losses") arising from claims, lawsuits +and other legal actions brought by a third party against the Indemnified +Contributor to the extent caused by the acts or omissions of such +Commercial Contributor in connection with its distribution of the Program +in a commercial product offering. The obligations in this section do not +apply to any claims or Losses relating to any actual or alleged +intellectual property infringement. In order to qualify, an Indemnified +Contributor must: a) promptly notify the Commercial Contributor in +writing of such claim, and b) allow the Commercial Contributor to control, +and cooperate with the Commercial Contributor in, the defense and any +related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those performance +claims and warranties, and if a court requires any other Contributor to +pay any damages as a result, the Commercial Contributor must pay +those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" +BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF +TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR +PURPOSE. Each Recipient is solely responsible for determining the +appropriateness of using and distributing the Program and assumes all +risks associated with its exercise of rights under this Agreement, +including but not limited to the risks and costs of program errors, +compliance with applicable laws, damage to or loss of data, programs +or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT +PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS +SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further +action by the parties hereto, such provision shall be reformed to the +minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software +or hardware) infringes such Recipient's patent(s), then such Recipient's +rights granted under Section 2(b) shall terminate as of the date such +litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of +time after becoming aware of such noncompliance. If all Recipient's +rights under this Agreement terminate, Recipient agrees to cease use +and distribution of the Program as soon as reasonably practicable. +However, Recipient's obligations under this Agreement and any licenses +granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, +but in order to avoid inconsistency the Agreement is copyrighted and +may only be modified in the following manner. The Agreement Steward +reserves the right to publish new versions (including revisions) of +this Agreement from time to time. No one other than the Agreement +Steward has the right to modify this Agreement. The Eclipse Foundation +is the initial Agreement Steward. The Eclipse Foundation may assign the +responsibility to serve as the Agreement Steward to a suitable separate +entity. Each new version of the Agreement will be given a distinguishing +version number. The Program (including Contributions) may always be +Distributed subject to the version of the Agreement under which it was +received. In addition, after a new version of the Agreement is published, +Contributor may elect to Distribute the Program (including its +Contributions) under the new version. + +Except as expressly stated in Sections 2(a) and 2(b) above, Recipient +receives no rights or licenses to the intellectual property of any +Contributor under this Agreement, whether expressly, by implication, +estoppel or otherwise. All rights in the Program not expressly granted +under this Agreement are reserved. Nothing in this Agreement is intended +to be enforceable by any entity that is not a Contributor or Recipient. +No third-party beneficiary rights are created under this Agreement. + +Exhibit A - Form of Secondary Licenses Notice + +"This Source Code may also be made available under the following +Secondary Licenses when the conditions for such availability set forth +in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), +version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. \ No newline at end of file diff --git a/core/src/test/resources/licenses/http___www_gnu_org_licenses_old_licenses_lgpl_2_1_txt b/core/src/test/resources/licenses/http___www_gnu_org_licenses_old_licenses_lgpl_2_1_txt new file mode 100644 index 00000000..4362b491 --- /dev/null +++ b/core/src/test/resources/licenses/http___www_gnu_org_licenses_old_licenses_lgpl_2_1_txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! From b98865ebb5fc90de160bf3a0fadbbae48b425ab0 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 3 Apr 2024 17:04:45 +0200 Subject: [PATCH 115/139] Java 8 deprecation Stage 1 (#251) --- .../devonfw/tools/solicitor/Solicitor.java | 19 +++++++++++++++++++ .../tools/solicitor/common/LogMessages.java | 2 +- documentation/master-solicitor.asciidoc | 4 +++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java index 2acfc574..0be13e4f 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java @@ -14,6 +14,7 @@ import com.devonfw.tools.solicitor.SolicitorCliProcessor.CommandLineOptions; import com.devonfw.tools.solicitor.SolicitorSetup.ReaderSetup; +import com.devonfw.tools.solicitor.common.DeprecationChecker; import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.common.MavenVersionHelper; import com.devonfw.tools.solicitor.common.ResourceToFileCopier; @@ -66,6 +67,9 @@ public class Solicitor { @Autowired private LifecycleListenerHolder lifecycleListenerHolder; + @Autowired + private DeprecationChecker deprecationChecker; + private boolean tolerateMissingInput = false; @Value("${solicitor.tolerate-missing-input}") @@ -111,6 +115,7 @@ private void extractConfig(String targetDir) { */ private void mainProcessing(CommandLineOptions clo) { + checkJavaVersion(); ModelRoot modelRoot = this.configFactory.createConfig(clo.configUrl); this.lifecycleListenerHolder.modelRootInitialized(modelRoot); if (clo.load) { @@ -136,6 +141,20 @@ private void mainProcessing(CommandLineOptions clo) { this.lifecycleListenerHolder.endOfMainProcessing(modelRoot); } + /** + * Checks the java version and possibly issue deprecation error or warning. + */ + private void checkJavaVersion() { + + String javaVersion = System.getProperty("java.version"); + // we just check for the prefix "1." because from Java 9 on the version number does no longer start with "1." + if (javaVersion.startsWith("1.")) { + this.deprecationChecker.check(true, + "Running Solicitor on Java 8 is deprecated. Yours is '" + javaVersion + "'. Switch to Java 11!"); + } + + } + /** * Read the inventory of {@link ApplicationComponent}s and their declared licenses. */ diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 311232ac..29205d6b 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -60,7 +60,7 @@ public enum LogMessages { DEPRECATIONS_ACTIVE(42, "Deprecated features are available ({}). Please check if this is necessary. Look for message code '{}' to check for actually used deprecated features."), // UNAVAILABLE_DEPRECATED_FEATURE(43, - "This featue is deprecated and no longer available. Details: {}. For backward compatibility you might temporary activate it by setting '{}'."), // + "This feature is deprecated and no longer available. Details: {}. For backward compatibility you might temporary activate it by setting '{}'."), // USING_DEPRECATED_FEATURE_FORCED(44, "You are using a deprecated feature which is only available because you set {}. You should ASAP migrate your project as this might be unavailable in future versions. Details: {}."), // MISSING_INVENTORY_INPUT_FILE(45, diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 099c11c1..4d422762 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -228,7 +228,7 @@ A "normalized" license id might be either a SPDX-Id, a `LicenseRef`-Id or a "pse == Usage === Executing Solicitor -_Solicitor_ is a standalone Java (Spring Boot) application. Prerequisite for running it is an existing Java 8 or 11 runtime environment. If you do not yet have a the _Solicitor_ executable JAR (`solicitor.jar`) you need to build it as given on the project GitHub homepage https://github.com/devonfw/solicitor . +_Solicitor_ is a standalone Java (Spring Boot) application. Prerequisite for running it is an existing Java 11 runtime environment. If you do not yet have a the _Solicitor_ executable JAR (`solicitor.jar`) you need to build it as given on the project GitHub homepage https://github.com/devonfw/solicitor . _Solicitor_ is executed with the following command: @@ -1371,6 +1371,7 @@ The following features are deprecated via the above mechanism: * "REGEX:" prefix notation in rule templates (use "(REGEX)" suffix instead); Stage 1 from Version 1.3.0 on; see https://github.com/devonfw/solicitor/issues/78 * Use of `LicenseAssignmentProject.xls` decision table (use `LicenseAssignmentV2Project.xls` instead); Stage 1 from Version 1.4.0 on * Use of `repoType` in the configuration of readers, see <>; Stage 1 from Version 1.14.0 on; see https://github.com/devonfw/solicitor/issues/190 +* Running Solicitor on Java 8; Stage 1 from Version 1.22.0 on; see https://github.com/devonfw/solicitor/issues/247 == Experimental Scancode Integration @@ -1701,6 +1702,7 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.22.0:: * https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. * https://github.com/devonfw/solicitor/pull/244: Updated Drools rule engine to 8.44.0.Final. +* https://github.com/devonfw/solicitor/issues/247: Running Solicitor on Java 8 is deprecated (Stage 1) and will be no longer possible soon. Move to Java 11 ASAP! Changes in 1.21.0:: * https://github.com/devonfw/solicitor/pull/239: Improving some internal components to reduce risk of path traversal attacks in case that these components are (re)used in some webservice implementation. From 25be0d7c97b979942567d1c8eca839edd9558989 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:11:37 +0200 Subject: [PATCH 116/139] Downgrade drools to 8.27.0.Beta to ensure Java 8 compatibility (#253) * Use drools 8.27.0.Beta instead of 8.44.0.Finals as 8.28.0 or higher breaks Java 8 compatibility. * Workaround to avoid https://issues.redhat.com/browse/DROOLS-7210 --- core/pom.xml | 6 +++--- .../solicitor/ruleengine/drools/DroolsRuleEngine.java | 7 +++++++ documentation/master-solicitor.asciidoc | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index b667b794..231cbed6 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -73,17 +73,17 @@ org.kie kie-ci - 8.44.0.Final + 8.27.0.Beta org.drools drools-decisiontables - 8.44.0.Final + 8.27.0.Beta org.drools drools-xml-support - 8.44.0.Final + 8.27.0.Beta org.apache.poi diff --git a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java index cc4d3eac..6babf193 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/DroolsRuleEngine.java @@ -19,6 +19,7 @@ import org.kie.api.io.Resource; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; +import org.kie.internal.builder.conf.ParallelRulesBuildThresholdOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -49,6 +50,12 @@ @Order(InventoryProcessor.RULE_ENGINE) public class DroolsRuleEngine implements RuleEngine { + static { + // avoid parallel compiling of rules to avoid bug existing in Drools 8.17.0.Beta ... 8.31.0.Beta + // see https://issues.redhat.com/browse/DROOLS-7210 + System.setProperty(ParallelRulesBuildThresholdOption.PROPERTY_NAME, "-1"); + } + private static final Logger LOG = LoggerFactory.getLogger(DroolsRuleEngine.class); @Value("${drools-rule-engine.debuglog}") diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 4d422762..6193d6e0 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1701,7 +1701,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.22.0:: * https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. -* https://github.com/devonfw/solicitor/pull/244: Updated Drools rule engine to 8.44.0.Final. +* https://github.com/devonfw/solicitor/pull/244: Updated Drools rule engine to 8.27.0.Beta. * https://github.com/devonfw/solicitor/issues/247: Running Solicitor on Java 8 is deprecated (Stage 1) and will be no longer possible soon. Move to Java 11 ASAP! Changes in 1.21.0:: From 697305f061db5bac7b1e9465de8e19e348062e22 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:02:37 +0200 Subject: [PATCH 117/139] Refactoring: Introduction of CurationDataHandle to enable additional ways to represent/reference curation data (#254) --- .../ComponentInfoInventoryProcessor.java | 3 +- .../componentinfo/ComponentInfoProvider.java | 6 ++-- .../componentinfo/CurationDataHandle.java | 8 +++++ .../SelectorCurationDataHandle.java | 34 +++++++++++++++++++ .../curation/ComponentInfoCurator.java | 7 ++-- .../curation/ComponentInfoCuratorImpl.java | 9 +++-- .../CuratingComponentInfoAdapter.java | 11 +++--- .../curation/CurationProvider.java | 10 +++--- .../curation/SingleFileCurationProvider.java | 9 +++-- ...FilteredScancodeComponentInfoProvider.java | 15 ++++---- .../SingleFileCurationProviderTest.java | 10 ++++-- ...redScancodeComponentInfoProviderTests.java | 10 ++++-- .../ScancodeComponentInfoAdapterTest.java | 21 +++++++----- documentation/master-solicitor.asciidoc | 5 +-- 14 files changed, 107 insertions(+), 51 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/CurationDataHandle.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/SelectorCurationDataHandle.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 0368b880..1def280f 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -132,7 +132,8 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { ComponentInfoData componentInfoData = null; try { for (ComponentInfoProvider cia : this.componentInfoAdapters) { - componentInfoCandidate = cia.getComponentInfo(ac.getPackageUrl(), this.curationDataSelector); + componentInfoCandidate = cia.getComponentInfo(ac.getPackageUrl(), + new SelectorCurationDataHandle(this.curationDataSelector)); if (componentInfoCandidate != null) { componentInfo = componentInfoCandidate; // stop querying further adapters if some info was returned diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java index 125eb395..6ddc9d7e 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java @@ -12,8 +12,7 @@ public interface ComponentInfoProvider { * {@link ComponentInfo} object. * * @param packageUrl The identifier of the package for which information is requested - * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. + * @param curationDataHandle identifies which source should be used for the curation data. * @return the data for the component. If the bean implementing this interface is deactivated (e.g. via some feature * flag configuration) this method will return null. Otherwise a non-null object * will be refurned. If there is no actual data available for the requested component calling @@ -21,6 +20,7 @@ public interface ComponentInfoProvider { * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be returned in such a case. */ - ComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) throws ComponentInfoAdapterException; + ComponentInfo getComponentInfo(String packageUrl, CurationDataHandle curationDataHandle) + throws ComponentInfoAdapterException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/CurationDataHandle.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/CurationDataHandle.java new file mode 100644 index 00000000..c2a325b6 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/CurationDataHandle.java @@ -0,0 +1,8 @@ +package com.devonfw.tools.solicitor.componentinfo; + +/** + * A handle which represents/references curation data. + */ +public interface CurationDataHandle { + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/SelectorCurationDataHandle.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/SelectorCurationDataHandle.java new file mode 100644 index 00000000..26b0fde3 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/SelectorCurationDataHandle.java @@ -0,0 +1,34 @@ +package com.devonfw.tools.solicitor.componentinfo; + +/** + * An implementation of a {@link CurationDataHandle} which references curation data via a dataSelector. This + * dataSelector is a string which might e.g. denote a branch in a Git repository. Further information (like e.g. the + * PackageURL) is needed to retrieve the curation data. + */ +public class SelectorCurationDataHandle implements CurationDataHandle { + + private String curationDataSelector; + + /** + * The constructor. + * + * @param curationDataSelector the curationDataSelector which is references the curation data. null + * indicates that the default should be used. "none" indicates that no curation should be applied. + */ + public SelectorCurationDataHandle(String curationDataSelector) { + + this.curationDataSelector = curationDataSelector; + + } + + /** + * Gets the curationDataSelctor encapsulated by this handle. + * + * @return the curationDataSelector + */ + public String getCurationDataSelector() { + + return this.curationDataSelector; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java index a0b60598..7a964784 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java @@ -2,6 +2,7 @@ import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; /** * A {@link ComponentInfoCurator} curates the {@link ComponentInfo} data. @@ -13,11 +14,11 @@ public interface ComponentInfoCurator { * Curates the given {@link ComponentInfo}. * * @param componentInfo the componentInfo to curate - * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. + * @param curationDataHandle identifies which source should be used for the curation data. * @return the curated component info or - if no curation are done - the incoming object * @throws ComponentInfoAdapterException if the curation could not be read */ - ComponentInfo curate(ComponentInfo componentInfo, String curationDataSelector) throws ComponentInfoAdapterException; + ComponentInfo curate(ComponentInfo componentInfo, CurationDataHandle curationDataHandle) + throws ComponentInfoAdapterException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java index 036b4650..a8f47574 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java @@ -8,6 +8,7 @@ import com.devonfw.tools.solicitor.componentinfo.ComponentContentProvider; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.DataStatusValue; import com.devonfw.tools.solicitor.componentinfo.DefaultComponentInfoImpl; import com.devonfw.tools.solicitor.componentinfo.DefaultLicenseInfoImpl; @@ -48,18 +49,16 @@ public ComponentInfoCuratorImpl(CurationProvider curationProvider, * and the curation. * * @param componentInfo the componentInfo to curate - * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. The special value "none" indicates that no curations will be - * applied. + * @param curationDataHandle identifies which source should be used for the curation data. * @return the curated component info * @throws ComponentInfoAdapterException if the curation could not be read */ @Override - public ComponentInfo curate(ComponentInfo componentInfo, String curationDataSelector) + public ComponentInfo curate(ComponentInfo componentInfo, CurationDataHandle curationDataHandle) throws ComponentInfoAdapterException { ComponentInfoCuration foundCuration = this.curationProvider.findCurations(componentInfo.getPackageUrl(), - curationDataSelector); + curationDataHandle); if (foundCuration != null) { DefaultComponentInfoImpl componentInfoImpl = new DefaultComponentInfoImpl(componentInfo); applyFoundCurations(componentInfoImpl, foundCuration); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java index c44c24de..30b84db5 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java @@ -11,6 +11,7 @@ import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapter; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.DataStatusValue; import com.devonfw.tools.solicitor.componentinfo.DefaultComponentInfoImpl; import com.devonfw.tools.solicitor.componentinfo.LicenseInfo; @@ -50,24 +51,22 @@ public CuratingComponentInfoAdapter(FilteredComponentInfoProvider filteredCompon * data as a {@link ComponentInfo} object. * * @param packageUrl The identifier of the package for which information is requested - * @param curationDataSelector Identifies which source should be used for the curation data. null - * indicates that the default should be used. Use "none" to indicate that no curation should be applied. + * @param curationDataHandle Identifies which source should be used for the curation data. * @return the data derived from the scancode results after applying any defined curation. * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be return in such a case. */ @Override - public ComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) + public ComponentInfo getComponentInfo(String packageUrl, CurationDataHandle curationDataHandle) throws ComponentInfoAdapterException { if (isFeatureActive()) { - ComponentInfo componentInfo = this.filteredComponentInfoProvider.getComponentInfo(packageUrl, - curationDataSelector); + ComponentInfo componentInfo = this.filteredComponentInfoProvider.getComponentInfo(packageUrl, curationDataHandle); if (componentInfo == null || componentInfo.getComponentInfoData() == null) { return componentInfo; } - componentInfo = this.componentInfoCurator.curate(componentInfo, curationDataSelector); + componentInfo = this.componentInfoCurator.curate(componentInfo, curationDataHandle); componentInfo = checkForIssues(componentInfo); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java index 8f2d9988..29880a93 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java @@ -2,6 +2,7 @@ import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; /** @@ -14,16 +15,13 @@ public interface CurationProvider { * Return the curation data for a given package. * * @param packageUrl identifies the package - * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. The special value "none" indicates that no curations should be - * returned. - * @return the curation data if it exists. null if no curation exist for the package or the - * curationDataSelector was given as "none". + * @param curationDataHandle identifies which source should be used for the curation data. + * @return the curation data if it exists. null if no curation exist for the package. * @throws ComponentInfoAdapterException if something unexpected happens * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException if the specified curationDataSelector could * not be resolved */ - ComponentInfoCuration findCurations(String packageUrl, String curationDataSelector) + ComponentInfoCuration findCurations(String packageUrl, CurationDataHandle curationDataHandle) throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java index 9667a828..b72fd9ab 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java @@ -14,6 +14,8 @@ import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; +import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; import com.devonfw.tools.solicitor.componentinfo.curation.model.CurationList; import com.fasterxml.jackson.databind.ObjectMapper; @@ -56,16 +58,17 @@ public SingleFileCurationProvider(AllKindsPackageURLHandler packageURLHandler) { * Return the curation data for a given package. * * @param packageUrl identifies the package - * @param curationDataSelector identifies which source should be used for the curation data. The value "none" - * indicates that no curations should be returned. + * @param curationDataHandle identifies which source should be used for the curation data. * @return the curation data if it exists. null if no curation exist for the package or the * curationDataSelector was given as "none". * @throws ComponentInfoAdapterException if something unexpected happens */ @Override - public ComponentInfoCuration findCurations(String packageUrl, String curationDataSelector) + public ComponentInfoCuration findCurations(String packageUrl, CurationDataHandle curationDataHandle) throws ComponentInfoAdapterException { + // SelectorCurationDataHandle is the only implementation supported here. + String curationDataSelector = ((SelectorCurationDataHandle) curationDataHandle).getCurationDataSelector(); // Return null if curationDataSelector is "none" if ("none".equalsIgnoreCase(curationDataSelector)) { return null; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java index 4660d3a4..8102cdd5 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java @@ -14,6 +14,7 @@ import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.DataStatusValue; import com.devonfw.tools.solicitor.componentinfo.DefaultComponentInfoImpl; import com.devonfw.tools.solicitor.componentinfo.curation.CurationProvider; @@ -93,14 +94,13 @@ public void setMinLicensefileNumberOfLines(int minLicensefileNumberOfLines) { * Read scancode information for the given package from local file storage. * * @param packageUrl The package url of the package - * @param curationDataSelector identifies which source should be used for the curation data. null - * indicates that the default should be used. "none" indicates that no curations should be applied. + * @param curationDataHandle identifies which source should be used for the curation data. * @return the read scancode information * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be returned in such a case. */ @Override - public ComponentInfo getComponentInfo(String packageUrl, String curationDataSelector) + public ComponentInfo getComponentInfo(String packageUrl, CurationDataHandle curationDataHandle) throws ComponentInfoAdapterException { ScancodeRawComponentInfo rawScancodeData; @@ -114,7 +114,7 @@ public ComponentInfo getComponentInfo(String packageUrl, String curationDataSele } ScancodeComponentInfo componentScancodeInfos = parseAndMapScancodeJson(packageUrl, rawScancodeData, - curationDataSelector); + curationDataHandle); addSupplementedData(rawScancodeData, componentScancodeInfos); LOG.debug("Scancode info for package {}: {} license, {} copyrights, {} NOTICE files", packageUrl, componentScancodeInfos.getComponentInfoData().getLicenses().size(), @@ -140,13 +140,12 @@ private void addSupplementedData(ScancodeRawComponentInfo rawScancodeData, * * @param packageUrl package URL of the package * @param rawScancodeData raw scancode data - * @param curationDataSelector identifies which source should be used for the curation data. If the value of - * curationdataselector equals "none," no curations will be applied. + * @param curationDataHandle identifies which source should be used for the curation data. * @return the ScancodeComponentInfo * @throws ComponentInfoAdapterException if there was an issue during parsing */ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, ScancodeRawComponentInfo rawScancodeData, - String curationDataSelector) throws ComponentInfoAdapterException { + CurationDataHandle curationDataHandle) throws ComponentInfoAdapterException { ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore, this.minLicensefileNumberOfLines); @@ -158,7 +157,7 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod ScancodeComponentInfoData scancodeComponentInfoData = componentScancodeInfos.getComponentInfoData(); // Get the curation for a given packageUrl - ComponentInfoCuration componentInfoCuration = this.curationProvider.findCurations(packageUrl, curationDataSelector); + ComponentInfoCuration componentInfoCuration = this.curationProvider.findCurations(packageUrl, curationDataHandle); // Get all excludedPaths in this curation List excludedPaths = null; diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java index de1f8e06..e95082ac 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java @@ -9,6 +9,7 @@ import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; @@ -44,9 +45,11 @@ void testFindCurationsWithSelectorNull() throws ComponentInfoAdapterException { ComponentInfoCuration result; - result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.4", null); + result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.4", + new SelectorCurationDataHandle(null)); assertEquals("https://scancode-licensedb.aboutcode.org/apache-2.0.LICENSE", result.getLicenses().get(0).getUrl()); - result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.5", null); + result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.5", + new SelectorCurationDataHandle(null)); assertEquals("https://scancode-licensedb.aboutcode.org/bsd-simplified.LICENSE", result.getLicenses().get(0).getUrl()); } @@ -62,7 +65,8 @@ void testFindCurationsWithSelectorNone() throws ComponentInfoAdapterException { ComponentInfoCuration result; - result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.4", "none"); + result = this.objectUnderTest.findCurations("pkg:maven/somenamespace/somecomponent@2.3.4", + new SelectorCurationDataHandle("none")); assertNull(result); } } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java index 909a37cb..0d5cafdb 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java @@ -10,6 +10,7 @@ import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; /** @@ -57,7 +58,8 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx // when ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( - "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); // then assertNotNull(scancodeComponentInfo.getComponentInfoData()); @@ -83,7 +85,8 @@ public void testGetComponentInfoWithCurationsAndExclusions() throws ComponentInf .setCurationsFileName("src/test/resources/scancodefileadapter/curations_with_exclusions.yaml"); // when ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( - "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); // then assertNotNull(scancodeComponentInfo.getComponentInfoData()); @@ -110,7 +113,8 @@ public void testGetComponentInfoWithCurationsAndWithoutExclusions() throws Compo // when ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( - "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); // then assertNotNull(scancodeComponentInfo.getComponentInfoData()); diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java index 789528a4..ad713cba 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java @@ -17,6 +17,7 @@ import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; import com.devonfw.tools.solicitor.componentinfo.LicenseInfo; +import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCurator; import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCuratorImpl; import com.devonfw.tools.solicitor.componentinfo.curation.CurationProvider; @@ -81,8 +82,8 @@ public void testGetComponentInfoNaPackage() throws ComponentInfoAdapterException // given // when - ComponentInfo componentInfo = this.scancodeComponentInfoAdapter - .getComponentInfo("pkg:maven/com.devonfw.tools/unknown@0.1.0", "someCurationSelector"); + ComponentInfo componentInfo = this.scancodeComponentInfoAdapter.getComponentInfo( + "pkg:maven/com.devonfw.tools/unknown@0.1.0", new SelectorCurationDataHandle("someCurationSelector")); // then assertNull(componentInfo.getComponentInfoData()); @@ -102,7 +103,8 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx // when ComponentInfo componentInfo = this.scancodeComponentInfoAdapter.getComponentInfo( - "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); // then assertNotNull(componentInfo.getComponentInfoData()); @@ -148,7 +150,8 @@ public void testGetComponentCheckCurationDataSelector() throws ComponentInfoAdap // given CurationProvider curationProvider = Mockito.mock(CurationProvider.class); - when(curationProvider.findCurations(Mockito.anyString(), Mockito.anyString())).thenReturn(null); + when(curationProvider.findCurations(Mockito.anyString(), Mockito.any(SelectorCurationDataHandle.class))) + .thenReturn(null); this.componentInfoCuratorImpl = new ComponentInfoCuratorImpl(curationProvider, this.fileScancodeRawComponentInfoProvider); @@ -158,15 +161,16 @@ public void testGetComponentCheckCurationDataSelector() throws ComponentInfoAdap // when ComponentInfo componentInfo = this.scancodeComponentInfoAdapter.getComponentInfo( - "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); // then assertNotNull(componentInfo.getComponentInfoData()); ArgumentCaptor captor1 = ArgumentCaptor.forClass(String.class); - ArgumentCaptor captor2 = ArgumentCaptor.forClass(String.class); + ArgumentCaptor captor2 = ArgumentCaptor.forClass(SelectorCurationDataHandle.class); Mockito.verify(curationProvider, times(1)).findCurations(captor1.capture(), captor2.capture()); - assertEquals("someCurationSelector", captor2.getValue()); + assertEquals("someCurationSelector", captor2.getValue().getCurationDataSelector()); } @@ -180,7 +184,8 @@ public void testGetComponentInfoWithCurations() throws ComponentInfoAdapterExcep // when ComponentInfo componentInfo = this.scancodeComponentInfoAdapter.getComponentInfo( - "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "someCurationSelector"); + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); // then assertNotNull(componentInfo.getComponentInfoData()); diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 6193d6e0..19ad02db 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1701,8 +1701,9 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.22.0:: * https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. -* https://github.com/devonfw/solicitor/pull/244: Updated Drools rule engine to 8.27.0.Beta. -* https://github.com/devonfw/solicitor/issues/247: Running Solicitor on Java 8 is deprecated (Stage 1) and will be no longer possible soon. Move to Java 11 ASAP! +* https://github.com/devonfw/solicitor/pull/244, https://github.com/devonfw/solicitor/pull/253: Updated Drools rule engine to 8.27.0.Beta. +* https://github.com/devonfw/solicitor/issues/247: Running Solicitor on Java 8 is deprecated (Stage 1) and will be no longer possible soon. *Move to Java 11 ASAP!* +* https://github.com/devonfw/solicitor/pull/254: Refactoring: Introduction of CurationDataHandle to enable additional ways to represent/reference curation data. Changes in 1.21.0:: * https://github.com/devonfw/solicitor/pull/239: Improving some internal components to reduce risk of path traversal attacks in case that these components are (re)used in some webservice implementation. From c9ac86697bc8a53a7486017abbcba5fb37590b06 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:50:38 +0200 Subject: [PATCH 118/139] Version 1.22.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 40c2a208..102978fd 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.22.0-SNAPSHOT + 1.22.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 231cbed6..fb605b87 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.22.0-SNAPSHOT + 1.22.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 2e286130..5f544766 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.22.0-SNAPSHOT + 1.22.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 59e2196b..1a8dabe3 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.22.0-SNAPSHOT + 1.22.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 0c155e80..628241e4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.22.0-SNAPSHOT + 1.22.0 pom Solicitor Aggregator From cd99643572323ab733edcb8ad92d9784a1579723 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:57:18 +0200 Subject: [PATCH 119/139] Set version to SNAPSHOT of next minor release (1.23.0-SNAPSHOT) --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 102978fd..1ad39257 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.22.0 + 1.23.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index fb605b87..9db33775 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.22.0 + 1.23.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 19ad02db..4be2e419 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1699,6 +1699,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.23.0:: + Changes in 1.22.0:: * https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. * https://github.com/devonfw/solicitor/pull/244, https://github.com/devonfw/solicitor/pull/253: Updated Drools rule engine to 8.27.0.Beta. diff --git a/documentation/pom.xml b/documentation/pom.xml index 5f544766..fdad0297 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.22.0 + 1.23.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 1a8dabe3..363959ea 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.22.0 + 1.23.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 628241e4..82911816 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.22.0 + 1.23.0-SNAPSHOT pom Solicitor Aggregator From 0d4c358a27b5ae7b31901c098f180b2fa5c3a27e Mon Sep 17 00:00:00 2001 From: duph97 Date: Wed, 17 Apr 2024 17:49:06 +0200 Subject: [PATCH 120/139] Deprecate/guessed license url (#256) * Deprecate templates using "guessedLicenseUrl" * Remove writers using guessedLicenseUrl * Improve warn message * Add release note and deprecation notice * Add example for additionalWriter * Fix typo * Added deprecation checker to XlsWriter, made deprecation detection more robust, adopted documentation with respect to deprecation of license URL guessing --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../writer/velocity/VelocityWriter.java | 10 +++++- .../solicitor/writer/xls/ExcelWriter.java | 9 ++++- .../tools/solicitor/config/solicitor_base.cfg | 11 ------ documentation/files/solicitor_base.cfg | 14 +------- documentation/master-solicitor.asciidoc | 34 +++++++++++++++---- 5 files changed, 46 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java index 2aa878a1..29323185 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/writer/velocity/VelocityWriter.java @@ -17,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.devonfw.tools.solicitor.common.DeprecationChecker; import com.devonfw.tools.solicitor.common.IOHelper; import com.devonfw.tools.solicitor.common.InputStreamFactory; import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; @@ -36,6 +37,9 @@ public class VelocityWriter implements Writer { @Autowired private AllKindsPackageURLHandler packageURLHandler; + @Autowired + private DeprecationChecker deprecationChecker; + /** * {@inheritDoc} * @@ -75,7 +79,11 @@ public void writeReport(String templateSource, String target, Map>. -For a description of the URL guessing mechanism see <>. +For a description of the URL guessing mechanism (deprecated) see <>. ===== License types Defines the type of license @@ -1303,6 +1303,9 @@ When creating the resource or filename for given URLs in the above steps the fol ** the last 40 characters of the (too) long filename == Guessing of license URLs + +WARNING: This feature is deprecated and will be removed soon. + Fetching the license content `NormalizedLicense.effectiveNormalizedLicenseContent` based on the URL in `NormalizedLicense.effectiveNormalizedLicenseUrl` will often result in content which is in HTML format instead of plain text and is not properly rendered when included in reports. Sometimes the URL even does not point to the license text itself but just the homepage of the project. In general it is possible to manually correct this by editing the downloaded and cached content as described in the previous section. This approach might require a lot of manual work. _Solicitor_ therefore includes a mechanism named license url guessing which tries to guess an alternative license URL which should point to a representation of the content better suited for rendering. @@ -1337,8 +1340,23 @@ URL changed from https://github.com/some/project/blob/master/LICENSE to https:// It is possible to manually change this cached information and thus correct it - similar to manually correcting the license text as described above. -WARNING: License guessing is a new feature as of `Solicitor` 1.3.0. -The guessing algorithm might be modified in future versions without further notice which might result in different outcomes for the guessed URLs. +=== License guessing feature usage +WARNING: From version 1.23.0 on the license guessing logic is deprecated. No standard report will use the guessed properties. + +To use license guessing in a template, an `additionalWriter` (see <> ) needs to be set in the `solicitor.cfg`. Example: + +[listing] + "additionalWriters" : [ { + "type" : "velo", + "templateSource" : "classpath:com/devonfw/tools/solicitor/templates/Solicitor_Output_Template_Sample_v2.vm", + "target" : "${cfgdir}/output/OSS-Report_${project}_v2.html", + "description" : "The HTML OSS-Report", + "dataTables" : { + "MODELROOT" : "classpath:com/devonfw/tools/solicitor/sql/modelroot.sql", + "ENGAGEMENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_engagements.sql", + "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents_guessedlicenses.sql", + "UNIQUELICENSES" : "classpath:com/devonfw/tools/solicitor/sql/uniqueguessedlicenses.sql" + } ] == Feature Deprecation Within the lifecycle of the _Solicitor_ development features might be discontinued due @@ -1372,6 +1390,7 @@ The following features are deprecated via the above mechanism: * Use of `LicenseAssignmentProject.xls` decision table (use `LicenseAssignmentV2Project.xls` instead); Stage 1 from Version 1.4.0 on * Use of `repoType` in the configuration of readers, see <>; Stage 1 from Version 1.14.0 on; see https://github.com/devonfw/solicitor/issues/190 * Running Solicitor on Java 8; Stage 1 from Version 1.22.0 on; see https://github.com/devonfw/solicitor/issues/247 +* LicenseUrl guessing; Stage 1 from Version 1.23.0 on; see https://github.com/devonfw/solicitor/issues/255 == Experimental Scancode Integration @@ -1611,6 +1630,8 @@ Name, GroupId, Version, Application, License, LicenseUrl. === Solicitor_Output_Template_Sample_v2.vm Similar to the above but uses guessed license URLs and content, see <>. +As license URL guessing is deprecated this template is no longer included in the standard configuration. +For activation see <>. === Quality_Report.vm @@ -1700,6 +1721,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.23.0:: +* https://github.com/devonfw/solicitor/issues/255: Deprecate LicenseUrl guessing. Changes in 1.22.0:: * https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. From d786c0320fc91c939aa30d77eaa6de3fd58f3928 Mon Sep 17 00:00:00 2001 From: duph97 Date: Thu, 25 Apr 2024 22:31:22 +0200 Subject: [PATCH 121/139] Feature/generic excel writer (#259) Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../writer/genericxls/GenericExcelWriter.java | 115 ++++++++++++++++++ documentation/master-solicitor.asciidoc | 22 ++++ 2 files changed, 137 insertions(+) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/writer/genericxls/GenericExcelWriter.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/writer/genericxls/GenericExcelWriter.java b/core/src/main/java/com/devonfw/tools/solicitor/writer/genericxls/GenericExcelWriter.java new file mode 100644 index 00000000..89f6a8fe --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/writer/genericxls/GenericExcelWriter.java @@ -0,0 +1,115 @@ +/** + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.devonfw.tools.solicitor.writer.genericxls; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.poi.EncryptedDocumentException; +// usermodel api for creating, reading and modifying xls files +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.springframework.stereotype.Component; + +import com.devonfw.tools.solicitor.common.IOHelper; +import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.writer.Writer; +import com.devonfw.tools.solicitor.writer.data.DataTable; +import com.devonfw.tools.solicitor.writer.data.DataTableField; +import com.devonfw.tools.solicitor.writer.data.DataTableRow; + +/** + * A {@link Writer} which creates a generic XLS workbook with a separate sheet for each defined dataTable. Field data + * will be trimmed when exceeding more than 200 characters. This report is intended for debugging purposes. + */ +@Component +public class GenericExcelWriter implements Writer { + + /** + * {@inheritDoc} + * + * Accepted type is "xls". + */ + @Override + public boolean accept(String type) { + + return "genericxls".equals(type); + } + + private String trimToReasonableLength(String original) { + + String trimmed = original; + if (original.length() > 200) { + trimmed = "HASH: " + original.hashCode() + "----" + original.substring(0, 200); + } + return trimmed; + + } + + /** + * {@inheritDoc} + * + * This function will generate a generic report. + */ + @Override + public void writeReport(String templateSource, String target, Map dataTables) { + + try { + + Workbook wb = WorkbookFactory.create(true); + + for (Entry tableEntry : dataTables.entrySet()) { + Sheet sh = wb.createSheet(limitSheetNameLength(tableEntry.getKey())); + + DataTable dataTable = tableEntry.getValue(); + int rowIndex = 0; + Row row = sh.createRow(rowIndex++); + int columnIndex = 0; + for (String columnName : dataTable.getHeadRow()) { + Cell cell = row.createCell(columnIndex++); + cell.setCellValue(columnName); + } + for (DataTableRow dataTableRow : dataTable) { + row = sh.createRow(rowIndex++); + for (int i = 0; i < dataTableRow.getSize(); i++) { + Cell cell = row.createCell(i); + DataTableField value = dataTableRow.getValueByIndex(i); + String textValue = value.toString() == null ? "" : value.toString(); + cell.setCellValue(trimToReasonableLength(textValue)); + } + } + } + + // Write the output to a file + IOHelper.checkAndCreateLocation(target); + try (OutputStream fileOut = new FileOutputStream(target)) { + wb.write(fileOut); + } + } catch (IOException | EncryptedDocumentException e) { + throw new SolicitorRuntimeException("Processing of XLS report failed", e); + } + + } + + /** + * @param name + * @return + */ + private String limitSheetNameLength(String name) { + + if (name.length() > 31) { + return (name.substring(0, 14) + "..." + name.substring(name.length() - 14)); + } else { + return name; + } + } + +} diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 56f05684..d0d6ae5b 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1281,6 +1281,26 @@ as follows when using the XLS templating: * For rows that are "new" (so no corresponding old row available) an Excel note indicating that this row is new will be attached to the field that contained the `\#...#` placeholder. * Fields in non-new rows that have changed their value will be marked with an Excel note indicating the old value. +==== Generic Excel Writer +The Generic Excel Writer exists purely for debugging purposes. This writer writes the contents of the `dataTables` defined in the writer configuration to an Excel file. Each `dataTable` will be available in a separate Excel sheet. To use this writer, an `additionalWriter` (see <> ) needs to be set in the `solicitor.cfg`. Example: +[listing] + "additionalWriters" : [ { + "type" : "genericxls", + "templateSource" : "", <1> + "target" : "${cfgdir}/output/GenericXLS.xlsx", + "description" : "Excel workbook with a separate sheet for each defined dataTable", + "dataTables" : { + "ENGAGEMENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_engagements.sql", + "APPLICATIONCOMPONENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_applicationcomponents.sql", + "LICENSE" : "classpath:com/devonfw/tools/solicitor/sql/allden_normalizedlicenses.sql", + "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql", + ... + } + } ] + +<1> This is unused and can be left empty. + + == Resolving of License URLs Resolving of the content of license texts which are referenced by the URLs given in `NormalizedLicense.effectiveNormalizedLicenseUrl` and `NormalizedLicense.licenseRefUrl` is done in the following way: @@ -1356,6 +1376,7 @@ To use license guessing in a template, an `additionalWriter` (see < Date: Fri, 26 Apr 2024 14:05:01 +0200 Subject: [PATCH 122/139] Improving usage pattern documentation (#261) * Extended documentation with respect to Semantics of usage pattern values * Changed reference in documentation to new story * added word to dictionary --- documentation/master-solicitor.asciidoc | 22 ++++++++++++++++++---- solicitor.dict | 1 + 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index d0d6ae5b..8e4419a9 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -350,12 +350,25 @@ Within this section the different applications (=deliverables) of the engagement <5> multiple readers might be defined per application <6> the type of reader; for possible values see <> <7> location of the source file to read (ResourceLoader-URL) -<8> usage pattern; possible values: DYNAMIC_LINKING, STATIC_LINKING, STANDALONE_PRODUCT +<8> usage pattern; possible values: `DYNAMIC_LINKING`, `STATIC_LINKING`, `STANDALONE_PRODUCT`; see description below <9> repoType: `repoType` to be set in the `ApplicationComponent` . *This parameter is deprecated and should no longer be used*, see <>. The value of `repoType` in `ApplicationComponent` will otherwise be determined from the type info in the PackageURL of the component. <10> _placeholder patterns might be used here_ +===== Usage Patterns +The usage pattern describes how the ``ApplicationComponent``s (libraries, packages) which are read in via the Reader are linked (in)to the ``Application``s executable. The kind of linking might affect the legal evaluation of the license compliance. -The different readers are described in chapter <> +* `DYNAMIC_LINKING` - The component is dynamically linked and is separated/separable from the rest of the executable and might be exchanged. +This specifically covers two cases: +** The component is not included in the executable but is either already existing on the target system or is deployed separately from the executable. Exchanging the component can be done by replacing the component without touching the executable / other components of the application. +** The component is included in the executable and is linked into the executable in a way that allows it to clearly distinguish it from the other components. It is possible to separate the component from the rest of the executable and to replace the component with a modified version of the component just using common tooling. +* `STATIC_LINKING` - The component is linked into the executable in a way that makes it (practically) impossible to separate it from the rest of the executable. In case that this single component needs to be replaced the linking process has to be re-executed based on the (unlinked) components. De facto this means that separating and/or exchanging the single components with only the executable at hand is practically impossible. +* `STANDALONE_PRODUCT` - The component is not linked to other components. It is executed in its own process. + +WARNING: The semantics of `DYNAMIC_LINKING` and `STATIC_LINKING` within Solicitor might differ from the common software engineers technical understanding of dynamic and static linking. +The main characteristics important in this context are given above. +As the legal evaluation of OSS license compliance might rely on the correct specification of the usage pattern you should consult the person being responsible for the legal evaluation if you are not sure about the right value. (Or in case that you are responsible for the legal evaluation: Make sure that the understanding of the possible usage pattern values corresponds to the legal evaluation rules you have defined.) + +The different readers are described in chapter <>. ==== Business Rules @@ -1520,7 +1533,7 @@ When using the Scancode integration the following values are used for field `App === Automatic mapping of `RawLicense` data obtained from Scancode to `NormalizedLicense` -Within the normal workflow `NormalizedLicense` objects are created from `RawLicense` objects via the rules given in the different LicenseAssignment and LicenseNameMapping decision tables, see <>. The "raw" license data obtained from Scancode represents licenses either by SPDX-IDs or (if licenses +Within the normal workflow `NormalizedLicense` objects are created from `RawLicense` objects via the rules given in the different LicenseAssignment and LicenseNameMapping decision tables, see <>. The "raw" license data obtained from Scancode represents licenses either by SPDX-IDs or (if licenses are detected which do not have a corresponding SPDX-IDs) via `LicenseRef-scancode-XXXXX` qualifiers. This is an improved data quality as compared to `RawLicenses` obtained from normal Readers. (See <>.) _Solicitor_ makes use of this improved data quality and by default performs an automatic mapping of `RawLicense` data to `NormalizedLicense` s in this case: * If the raw license matches a SPDX-ID then a `NormalizedLicense` is created with `normalizedLicenseType` set to `OSS-SPDX`. @@ -1596,7 +1609,7 @@ Using the <> it is possible to qualify whether a rul | _(empty)_ | ... in both cases |==================== -Due the automatic mapping of scancode based `RawLicenses` to `NormalizedLicenses` (see <>) such explicit mapping rules are only required for licenses not handled by the automatism. +Due the automatic mapping of scancode based `RawLicenses` to `NormalizedLicenses` (see <>) such explicit mapping rules are only required for licenses not handled by the automatism. [appendix] == Default Base Configuration @@ -1744,6 +1757,7 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.23.0:: * https://github.com/devonfw/solicitor/issues/255: Deprecate LicenseUrl guessing. * https://github.com/devonfw/solicitor/issues/258: Add GenericExcelWriter for debugging of SQL scripts. +* https://github.com/devonfw/solicitor/issues/260: Extended the user guide towards semantics of <>. Changes in 1.22.0:: * https://github.com/devonfw/solicitor/pull/243: Make sure the MavenReader is protected against XXE threats. diff --git a/solicitor.dict b/solicitor.dict index 1fbf192f..702356ea 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -8,6 +8,7 @@ deliverables Dloader Dpropertyname Dsome +De dt drt ec From 6e4cc30f1322d6452b879caef253e240afa2277b Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 6 May 2024 17:08:26 +0200 Subject: [PATCH 123/139] Version 1.23.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 1ad39257..6d9f9ab3 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.23.0-SNAPSHOT + 1.23.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 9db33775..78bd754a 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.23.0-SNAPSHOT + 1.23.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index fdad0297..852dfb07 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.23.0-SNAPSHOT + 1.23.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 363959ea..62d18ed5 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.23.0-SNAPSHOT + 1.23.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 82911816..b99a395e 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.23.0-SNAPSHOT + 1.23.0 pom Solicitor Aggregator From a62e5519fa591d3815c145c02b9fb5f7e1a9e632 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Mon, 6 May 2024 17:17:04 +0200 Subject: [PATCH 124/139] Set version to SNAPSHOT of next minor release (1.24.0-SNAPSHOT) --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 6d9f9ab3..5da36a5c 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.23.0 + 1.24.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 78bd754a..b426a53c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.23.0 + 1.24.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 8e4419a9..ae1700e4 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1754,6 +1754,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.24.0:: + Changes in 1.23.0:: * https://github.com/devonfw/solicitor/issues/255: Deprecate LicenseUrl guessing. * https://github.com/devonfw/solicitor/issues/258: Add GenericExcelWriter for debugging of SQL scripts. diff --git a/documentation/pom.xml b/documentation/pom.xml index 852dfb07..402ee609 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.23.0 + 1.24.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 62d18ed5..5e6e036b 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.23.0 + 1.24.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index b99a395e..e5d1a4bd 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.23.0 + 1.24.0-SNAPSHOT pom Solicitor Aggregator From a9432d56bf9a5c4c41ba842b6cdf191467af6abf Mon Sep 17 00:00:00 2001 From: duph97 Date: Thu, 23 May 2024 13:40:56 +0200 Subject: [PATCH 125/139] Deprecate features stage2 (#264) * Set deprecated features to stage 2 * Updated deprecation list and release notes * remove repotype from test config * Adopt documentation and log messages as proposed in my review comments --------- Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../tools/solicitor/config/ConfigFactory.java | 6 ++++-- .../npmlicensecrawler/NpmLicenseCrawlerReader.java | 12 ++++++++---- .../solicitor/ruleengine/drools/ModelHelper.java | 4 ++-- .../tools/solicitor/config/solicitor_base.cfg | 2 +- .../test/resources/ownlicenseinfo/solicitor_own.cfg | 3 +-- documentation/master-solicitor.asciidoc | 11 ++++++----- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java b/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java index ce58ec3c..e5a60228 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java @@ -126,8 +126,10 @@ public ModelRoot createConfig(String url) { rs.setSource(rc.getSource()); rs.setUsagePattern(rc.getUsagePattern()); if (rc.getRepoType() != null) { - this.deprecationChecker.check(true, - "The parameter 'repoType' in the reader configuration is deprecated, see https://github.com/devonfw/solicitor/issues/190"); + this.deprecationChecker.check(false, + "The parameter 'repoType' in the reader configuration is deprecated, see " + + "https://github.com/devonfw/solicitor/issues/190 and " + + "https://github.com/devonfw/solicitor/issues/263"); } rs.setRepoType(rc.getRepoType()); rs.setConfiguration(rc.getConfiguration()); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java index 9a95ee31..9ddb9be8 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java @@ -69,12 +69,16 @@ public Set getSupportedTypes() { public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, String repoType, Map configuration) { - this.deprecationChecker.check(true, "Use of Reader of type '" + SUPPORTED_TYPE - + "' is deprecated, use 'npm-license-checker' instead. See https://github.com/devonfw/solicitor/issues/125"); + this.deprecationChecker.check(false, + "Use of Reader of type '" + SUPPORTED_TYPE + + "' is deprecated, use 'npm-license-checker' instead. See https://github.com/devonfw/solicitor/issues/125" + + " and https://github.com/devonfw/solicitor/issues/263"); if (SUPPORTED_TYPE_DEPRECATED.equals(type)) { - this.deprecationChecker.check(true, "Use of type 'npm' is deprecated. Change type in config to '" + SUPPORTED_TYPE - + "'. See https://github.com/devonfw/solicitor/issues/62"); + this.deprecationChecker.check(false, + "Use of type 'npm' is deprecated. Change type in config to '" + SUPPORTED_TYPE + + "'. See https://github.com/devonfw/solicitor/issues/62 and " + + "https://github.com/devonfw/solicitor/issues/263"); } int components = 0; int licenses = 0; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/ModelHelper.java b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/ModelHelper.java index 3caed96b..6bea8fcc 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/ModelHelper.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/ruleengine/drools/ModelHelper.java @@ -200,8 +200,8 @@ public static boolean match(String input, String condition) { } if (input != null && condition != null) { if (condition.startsWith(REGEX_PREFIX)) { - deprecationChecker.check(true, - "Use of 'REGEX:' prefix notation is deprecated, use '(REGEX)' suffix instead. See https://github.com/devonfw/solicitor/issues/78"); + deprecationChecker.check(false, "Use of 'REGEX:' prefix notation is deprecated, use '(REGEX)' suffix instead. " + + "See https://github.com/devonfw/solicitor/issues/78 and https://github.com/devonfw/solicitor/issues/263"); String pattern = condition.substring(REGEX_PREFIX.length()); return input.matches(pattern); } diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg index d58b80d4..c7fe425e 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg +++ b/core/src/main/resources/com/devonfw/tools/solicitor/config/solicitor_base.cfg @@ -26,7 +26,7 @@ "templateSource" : "classpath:com/devonfw/tools/solicitor/rules/rule_templates/LicenseAssignment.drt", "ruleGroup" : "LicenseAssignmentProject", "description" : "setting license in case that no one was detected or overwriting a wrongly detected license (project overrides; 'old' rule structure)", - "deprecationWarnOnly" : true, + "deprecationWarnOnly" : false, "deprecationDetails" : "Use of decision table LicenseAssignmentProject is deprecated, use LicenseAssignmentV2Project instead. See release notes of release 1.4.0 for migration hints" },{ "type" : "dt", diff --git a/core/src/test/resources/ownlicenseinfo/solicitor_own.cfg b/core/src/test/resources/ownlicenseinfo/solicitor_own.cfg index ab7dc940..03b6c2ee 100644 --- a/core/src/test/resources/ownlicenseinfo/solicitor_own.cfg +++ b/core/src/test/resources/ownlicenseinfo/solicitor_own.cfg @@ -16,8 +16,7 @@ "readers" : [ { "type" : "maven", "source" : "file:target/generated-resources/licenses.xml", - "usagePattern" : "DYNAMIC_LINKING", - "repoType" : "maven" + "usagePattern" : "DYNAMIC_LINKING" } ] } ], "writers" : [ { diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index ae1700e4..450762e3 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1417,12 +1417,12 @@ wrong/misleading output) the first stage of deprecation will be skipped. === List of Deprecated Features The following features are deprecated via the above mechanism: -* Reader of type "npm-license-crawler-csv" (use Reader of type "npm-license-checker" instead); Stage 1 from Version 1.4.0 on; see https://github.com/devonfw/solicitor/issues/125 +* Reader of type "npm-license-crawler-csv" (use Reader of type "npm-license-checker" instead); Stage 2 from Version 1.24.0 on; see https://github.com/devonfw/solicitor/issues/125 and https://github.com/devonfw/solicitor/issues/263 * Reader of type "gradle" (use Reader of type "gradle2" instead); Stage 2 from Version 1.0.5 on; see https://github.com/devonfw/solicitor/issues/58 -* Reader of type "npm"; Stage 1 from Version 1.0.8 on; see https://github.com/devonfw/solicitor/issues/62 -* "REGEX:" prefix notation in rule templates (use "(REGEX)" suffix instead); Stage 1 from Version 1.3.0 on; see https://github.com/devonfw/solicitor/issues/78 -* Use of `LicenseAssignmentProject.xls` decision table (use `LicenseAssignmentV2Project.xls` instead); Stage 1 from Version 1.4.0 on -* Use of `repoType` in the configuration of readers, see <>; Stage 1 from Version 1.14.0 on; see https://github.com/devonfw/solicitor/issues/190 +* Reader of type "npm"; Stage 2 from Version 1.24.0 on; see https://github.com/devonfw/solicitor/issues/62 and https://github.com/devonfw/solicitor/issues/263 +* "REGEX:" prefix notation in rule templates (use "(REGEX)" suffix instead); Stage 2 from Version 1.24.0 on; see https://github.com/devonfw/solicitor/issues/78 and https://github.com/devonfw/solicitor/issues/263 +* Use of `LicenseAssignmentProject.xls` decision table (use `LicenseAssignmentV2Project.xls` instead); Stage 2 from Version 1.24.0 on; see https://github.com/devonfw/solicitor/issues/263 +* Use of `repoType` in the configuration of readers, see <>; Stage 2 from Version 1.24.0 on; see https://github.com/devonfw/solicitor/issues/190 and https://github.com/devonfw/solicitor/issues/263 * Running Solicitor on Java 8; Stage 1 from Version 1.22.0 on; see https://github.com/devonfw/solicitor/issues/247 * LicenseUrl guessing; Stage 1 from Version 1.23.0 on; see https://github.com/devonfw/solicitor/issues/255 @@ -1755,6 +1755,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes Changes in 1.24.0:: +* https://github.com/devonfw/solicitor/issues/263: Some features were pushed from deprecation stage 1 to stage 2, which means they do not longer work with default configuration: repoType attribute, npm and npm-license-crawler readers, REGEX prefix notation, LicenseAssignmentProject.xls. See <> for details. Changes in 1.23.0:: * https://github.com/devonfw/solicitor/issues/255: Deprecate LicenseUrl guessing. From a56a3e432cebee7875efa7591d55ebca79899a89 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 23 May 2024 14:44:58 +0200 Subject: [PATCH 126/139] Added some license name mappings (#265) * added some missing name mappings * Include link to PR in release notes --- .../rules/LicenseNameMappingSample.xls | Bin 202752 -> 204288 bytes documentation/master-solicitor.asciidoc | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls b/core/src/main/resources/com/devonfw/tools/solicitor/rules/LicenseNameMappingSample.xls index 47d7eb40b83233a4dd8922a7b6935ec0ab57b9f9..87ba9a7ae237b3f0a69451da21a758003b387d38 100644 GIT binary patch delta 18573 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 KRBy++;{Feak_k%y diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 450762e3..8bf6bb17 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1756,6 +1756,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes Changes in 1.24.0:: * https://github.com/devonfw/solicitor/issues/263: Some features were pushed from deprecation stage 1 to stage 2, which means they do not longer work with default configuration: repoType attribute, npm and npm-license-crawler readers, REGEX prefix notation, LicenseAssignmentProject.xls. See <> for details. +* https://github.com/devonfw/solicitor/pull/265: Added some license name mappings. Changes in 1.23.0:: * https://github.com/devonfw/solicitor/issues/255: Deprecate LicenseUrl guessing. @@ -1944,4 +1945,4 @@ Changes in 1.0.7:: * https://github.com/devonfw/solicitor/issues/56: Enable continuing analysis in multiapplication projects even is some license files are unavailable. * Described simplified usage of license-maven-plugin without need to change pom.xml. (Documentation only) -* Ensure consistent sorting even in case that multiple "Ignored" licenses exist for a component \ No newline at end of file +* Ensure consistent sorting even in case that multiple "Ignored" licenses exist for a component From ce867f7eb799745b0985f91091579c6a7404a93f Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 24 May 2024 10:35:46 +0200 Subject: [PATCH 127/139] Improve correlation of records (delta processing) for aggregated reports (#266) * Remove applicationName from correlation keys as this not a stable key in case of the performed aggregation * updated release notes --- .../sql/normalizedlicenses_aggregated_applications.sql | 8 ++++---- documentation/master-solicitor.asciidoc | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql b/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql index bd1baedb..a67e9a71 100644 --- a/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql +++ b/core/src/main/resources/com/devonfw/tools/solicitor/sql/normalizedlicenses_aggregated_applications.sql @@ -5,10 +5,10 @@ -- due to reading from different input files. -- This is a replacement of "allden_normalizedlicenses.sql" for generating aggregated reports. select - CONCAT(NVL("applicationName",'-'),NVL("groupId",'-'),NVL("artifactId",'-'),NVL("version",'-'),NVL("normalizedLicense",'-')) as CORR_KEY_0, - CONCAT(NVL("applicationName",'-'),NVL("groupId",'-'),NVL("artifactId",'-'),NVL("normalizedLicense",'-')) as CORR_KEY_1, - CONCAT(NVL("applicationName",'-'),NVL("groupId",'-'),NVL("artifactId",'-'),NVL("version",'-')) as CORR_KEY_2, - CONCAT(NVL("applicationName",'-'),NVL("groupId",'-'),NVL("artifactId",'-')) as CORR_KEY_3, + CONCAT(NVL("groupId",'-'),NVL("artifactId",'-'),NVL("version",'-'),NVL("normalizedLicense",'-')) as CORR_KEY_0, + CONCAT(NVL("groupId",'-'),NVL("artifactId",'-'),NVL("normalizedLicense",'-')) as CORR_KEY_1, + CONCAT(NVL("groupId",'-'),NVL("artifactId",'-'),NVL("version",'-')) as CORR_KEY_2, + CONCAT(NVL("groupId",'-'),NVL("artifactId",'-')) as CORR_KEY_3, "applicationName", "version" , "trace" , diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 8bf6bb17..78aaae4a 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1757,6 +1757,7 @@ Spring beans implementing this interface will be called at certain points in the Changes in 1.24.0:: * https://github.com/devonfw/solicitor/issues/263: Some features were pushed from deprecation stage 1 to stage 2, which means they do not longer work with default configuration: repoType attribute, npm and npm-license-crawler readers, REGEX prefix notation, LicenseAssignmentProject.xls. See <> for details. * https://github.com/devonfw/solicitor/pull/265: Added some license name mappings. +* https://github.com/devonfw/solicitor/pull/266: Improve correlation of records (diff processing, see <>) for aggregated inventory report `OSS-Inventory_aggregated_*.xlsx`. Changes in 1.23.0:: * https://github.com/devonfw/solicitor/issues/255: Deprecate LicenseUrl guessing. From 6096d2ae8b8a78d7e9b102ea73c99e89689034e0 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 11 Jun 2024 12:07:48 +0200 Subject: [PATCH 128/139] Curations via REMOVE, REPLACE, ADD and hierarchical curations (#269) --- .../tools/solicitor/common/LogMessages.java | 4 +- .../ComponentInfoInventoryProcessor.java | 3 + .../componentinfo/ComponentInfoProvider.java | 5 +- .../AbstractCurationProviderBase.java | 56 ++ .../AbstractHierarchicalCurationProvider.java | 241 +++++++ .../curation/ComponentInfoCurator.java | 3 +- .../curation/ComponentInfoCuratorImpl.java | 7 +- .../CuratingComponentInfoAdapter.java | 3 +- .../curation/CurationInvalidException.java | 28 + .../curation/CurationProvider.java | 4 +- .../componentinfo/curation/CurationUtil.java | 110 +++ .../curation/SingleFileCurationProvider.java | 65 +- .../curation/model/ComponentInfoCuration.java | 57 ++ .../curation/model/CopyrightCuration.java | 214 ++++++ .../curation/model/CurationOperation.java | 20 + .../curation/model/LicenseCuration.java | 320 +++++++++ ...FilteredScancodeComponentInfoProvider.java | 234 ++++++- .../scancode/ScancodeComponentInfo.java | 9 +- ...tractHierarchicalCurationProviderTest.java | 191 +++++ .../CurationReadingTest.java | 40 +- .../curation/CurationUtilTest.java | 101 +++ .../SingleFileCurationProviderTest.java | 30 +- .../curation/model/CopyrightCurationTest.java | 162 +++++ .../curation/model/LicenseCurationTest.java | 198 ++++++ ...redScancodeComponentInfoProviderTests.java | 12 +- .../scancode/RawCurationTest.java | 663 ++++++++++++++++++ .../ScancodeComponentInfoAdapterTest.java | 14 +- .../curations/array_of_curations.yaml | 14 + .../curation_with_all_fields_set.yaml | 38 + .../test/resources/curations/curations.txt | 12 + .../copyright_add_curation_1.yaml | 8 + .../copyright_add_curation_2.yaml | 7 + .../copyright_add_curation_3.yaml | 8 + .../copyright_remove_curation_1.yaml | 8 + .../copyright_remove_curation_2.yaml | 8 + .../copyright_remove_curation_4.yaml | 8 + .../copyright_remove_curation_7.yaml | 8 + .../copyright_replace_curation_1.yaml | 9 + .../rawcurations/license_add_curation_1.yaml | 8 + .../rawcurations/license_add_curation_2.yaml | 7 + .../rawcurations/license_add_curation_3.yaml | 8 + .../license_remove_curation_1.yaml | 10 + .../license_remove_curation_2.yaml | 10 + .../license_remove_curation_3.yaml | 10 + .../license_remove_curation_4.yaml | 10 + .../license_remove_curation_5.yaml | 10 + .../license_remove_curation_6.yaml | 10 + .../license_remove_curation_7.yaml | 10 + .../license_replace_curation_1.yaml | 12 + .../license_replace_curation_2.yaml | 12 + .../license_replace_curation_3.yaml | 12 + documentation/master-solicitor.asciidoc | 146 +++- solicitor.dict | 1 + 53 files changed, 3124 insertions(+), 74 deletions(-) create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractCurationProviderBase.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProvider.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationInvalidException.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtil.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CopyrightCuration.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CurationOperation.java create mode 100644 core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseCuration.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProviderTest.java rename core/src/test/java/com/devonfw/tools/solicitor/componentinfo/{curating => curation}/CurationReadingTest.java (58%) create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java rename core/src/test/java/com/devonfw/tools/solicitor/componentinfo/{curating => curation}/SingleFileCurationProviderTest.java (71%) create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CopyrightCurationTest.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseCurationTest.java create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/RawCurationTest.java create mode 100644 core/src/test/resources/curations/curations.txt create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_1.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_2.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_3.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_1.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_2.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_4.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_7.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/copyright_replace_curation_1.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_1.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_2.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_3.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_1.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_2.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_3.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_4.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_5.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_6.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_7.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_1.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_2.yaml create mode 100644 core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_3.yaml diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 29205d6b..0b4afff1 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -104,7 +104,9 @@ public enum LogMessages { "When reading yarn license info from file '{}' there was at least one virtual package encountered. Check if package resolution is correct"), // MODERN_YARN_PATCHED_PACKAGE(70, "When reading yarn license info from file '{}' there was at least one patched package encountered. Processing only the base package, not the patched version."), // - FAILED_READING_FILE(71, "Reading file '{}' failed"); + FAILED_READING_FILE(71, "Reading file '{}' failed"), // + EMPTY_PACKAGE_URL(72, "The package URL is null or empty."), // + EMPTY_PACKAGE_PATH(73, "The package path is null or empty."); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java index 1def280f..7edd82f4 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoInventoryProcessor.java @@ -12,6 +12,7 @@ import com.devonfw.tools.solicitor.InventoryProcessor; import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; import com.devonfw.tools.solicitor.model.ModelFactory; import com.devonfw.tools.solicitor.model.ModelRoot; import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; @@ -145,6 +146,8 @@ private Statistics processApplicationComponent(ApplicationComponent ac) { } } catch (ComponentInfoAdapterException e) { throw new SolicitorRuntimeException("Exception when reading component info data source", e); + } catch (CurationInvalidException e) { + throw new SolicitorRuntimeException("Curation data invalid when reading component info data source", e); } if (componentInfo == null) { // all adapters disabled diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java index 6ddc9d7e..eebc5c82 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/ComponentInfoProvider.java @@ -1,5 +1,7 @@ package com.devonfw.tools.solicitor.componentinfo; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; + /** * Provides {@link ComponentInfo} for components given by their PackageURL. Subinterfaces further specify if the * {@link ComponentInfo} is already curated or not. @@ -19,8 +21,9 @@ public interface ComponentInfoProvider { * {@link ComponentInfo#getComponentInfoData()} on the returned object will return null. * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be returned in such a case. + * @throws CurationInvalidException if the curation data is not valid */ ComponentInfo getComponentInfo(String packageUrl, CurationDataHandle curationDataHandle) - throws ComponentInfoAdapterException; + throws ComponentInfoAdapterException, CurationInvalidException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractCurationProviderBase.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractCurationProviderBase.java new file mode 100644 index 00000000..cf4912dd --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractCurationProviderBase.java @@ -0,0 +1,56 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; + +/** + * Abstract base implementation of {@link CurationProvider} to be used as starting point for concrete implementations. + * + */ +public abstract class AbstractCurationProviderBase implements CurationProvider { + + /** + * The constructor. + */ + public AbstractCurationProviderBase() { + + super(); + } + + /** + * Fetches curation data for a given package URL and returns it as a ComponentInfoCuration object. + *

        + * Method to be implemented in subclasses. + * + * It has the same functionality as {@link CurationProvider#findCurations(String, CurationDataHandle)} but does not + * require validating the fetched curation data. + * + * @param packageUrl The package URL for which curation data is to be fetched. + * @param curationDataHandle identifies which source should be used for the curation data. + * @return A ComponentInfoCuration object containing curation data. + * @throws ComponentInfoAdapterException If any errors occur during the retrieval or parsing of curation data. + * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException If the specified curation data selector does + * not exist. + */ + protected abstract ComponentInfoCuration doFindCurations(String packageUrl, CurationDataHandle curationDataHandle) + throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException; + + /** + * Implementation of {@link CurationProvider#findCurations(String, CurationDataHandle)} which delegates the fetching + * of the curations to (abstract) method {@link #doFindCurations(String, CurationDataHandle)} and then validates the + * result before returning. + */ + @Override + public ComponentInfoCuration findCurations(String packageUrl, CurationDataHandle curationDataHandle) + throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException, + CurationInvalidException { + + ComponentInfoCuration curation = doFindCurations(packageUrl, curationDataHandle); + if (curation != null) { + curation.validate(); + } + return curation; + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProvider.java new file mode 100644 index 00000000..539683f1 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProvider.java @@ -0,0 +1,241 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.common.packageurl.PackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; +import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; + +/** + * Abstract base implementation of {@link CurationProvider} which supports storing curations in a hierarchical structure + * so that curations are not required to be defined for specific component versions but might be defined for groups of + * components. The hierarchy is derived from the PackageURL, see {@link PackageURLHandler#pathFor(String)}. The + * {@link CurationProvider#findCurations(String, CurationDataHandle)} method of this class requires the + * {@link CurationDataHandle} to be a {@link SelectorCurationDataHandle}. + * + */ +public abstract class AbstractHierarchicalCurationProvider extends AbstractCurationProviderBase { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractHierarchicalCurationProvider.class); + + private final AllKindsPackageURLHandler packageURLHandler; + + /** + * The constructor. + * + * @param packageURLHandler the required packageURLHandler + */ + public AbstractHierarchicalCurationProvider(AllKindsPackageURLHandler packageURLHandler) { + + super(); + this.packageURLHandler = packageURLHandler; + } + + /** + * Fetches curation data for a given package URL from a curation repository and returns it as a ComponentInfoCuration + * object. + * + *

        + * This method fetches curation data for a given package URL from a Repository and returns it as a + * ComponentInfoCuration object. Fetching from a hierarchy and merging the different curation fragments is supported. + * In case that data is requested for a non default curationDataSelector which does not exist will result in an + * exception to be thrown. + *

        + * + * @param packageUrl The package URL for which curation data is to be fetched. + * @param curationDataHandle This has to be a {@link SelectorCurationDataHandle} and specifies the (alternative) + * location to take the curation data from. If the curationDataSelector is set to "none" then no curation will + * be returned. + * @return A ComponentInfoCuration object containing curation data (unvalidated). null if no curation + * data exists or the curationDataSelector of the curationDataHandle is set to "none". + * @throws ComponentInfoAdapterException If any errors occur during the retrieval of curation data. + * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException If the specified curationDataSelector does not + * exist. + */ + @Override + protected ComponentInfoCuration doFindCurations(String packageUrl, CurationDataHandle curationDataHandle) + throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException { + + LOG.debug("Received packageUrl: '{}'", packageUrl); + if (packageUrl == null || packageUrl.isEmpty()) { + LOG.error(LogMessages.EMPTY_PACKAGE_URL.msg()); + throw new ComponentInfoAdapterException("The package URL cannot be null or empty."); + } + + String curationDataSelector = ((SelectorCurationDataHandle) curationDataHandle).getCurationDataSelector(); + // Check if curation should not be applied + if ("none".equalsIgnoreCase(curationDataSelector)) { + LOG.debug("Retrieving curation is disabled for packageUrl '{}' due to curationDataSelector having value 'none'", + packageUrl); + return null; + } + String effectiveCurationDataSelector; + effectiveCurationDataSelector = determineEffectiveCurationDataSelector(curationDataSelector); + validateEffectiveCurationDataSelector(effectiveCurationDataSelector); + + // Parse the package URL to get the path to the curation file in the curation repository + String pathFromPackageUrl = this.packageURLHandler.pathFor(packageUrl); + LOG.debug("Parsed package path: '{}'", pathFromPackageUrl); + if (pathFromPackageUrl == null || pathFromPackageUrl.isEmpty()) { + LOG.error(LogMessages.EMPTY_PACKAGE_PATH.msg()); + throw new ComponentInfoAdapterException( + "The package path parsed from the package URL is null or empty for the package URL: " + packageUrl); + } + + List data = null; + try { + data = fetchAllDataFromCurationRepository(effectiveCurationDataSelector, pathFromPackageUrl); + } catch (CurationInvalidException e) { + throw new ComponentInfoAdapterException("Error while mapping JSON content", e.getCause()); + } + + if (data.isEmpty()) { + assureCurationDataSelectorAvailable(effectiveCurationDataSelector); + LOG.debug("NO curations found in Curations Repository for '{}' with curationDataSelector '{}'", packageUrl, + effectiveCurationDataSelector); + return null; + } + LOG.debug("Curations found in Curations Repository for '{}' with curationDataSelector '{}'", packageUrl, + effectiveCurationDataSelector); + + ComponentInfoCuration merged = mergeCurationData(data); + return merged; + } + + /** + * Fetches a single curation object (or curation fragment) from the hierarchy for the defined component. + *

        + * To be implemented by concrete subclasses. + * + * @param effectiveCurationDataSelector the effective curationDataSelector + * @param pathFragmentWithinRepo the path of the curation data or fragment. This will be either the value returned by + * {@link PackageURLHandler#pathFor(String)} or a (trailing) subpath. + * @return The found curation object (might be a fragment). null if nothing is defined. + * @throws ComponentInfoAdapterException if there was any unforeseen error when trying to retrieve the curation + * object. + * @throws CurationInvalidException if the curation data was malformed + */ + protected abstract ComponentInfoCuration fetchCurationFromRepository(String effectiveCurationDataSelector, + String pathFragmentWithinRepo) throws ComponentInfoAdapterException, CurationInvalidException; + + /** + * Validate the effective curationDataSelector. This needs to check is syntactically correct and might not trigger any + * unwanted effects e.g. like path traversal. + *

        + * To be implemented by concrete subclasses. + * + * @param effectiveCurationDataSelector the curationDataSelector to be validated + * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException if validation fails + */ + protected abstract void validateEffectiveCurationDataSelector(String effectiveCurationDataSelector) + throws ComponentInfoAdapterNonExistingCurationDataSelectorException; + + /** + * Determine the effective curationDataSelector. This specifically needs to handle the case that the value + * null is given which needs to be interpreted as "take the default". + *

        + * To be implemented by concrete subclasses. + * + * @param curationDataSelector the incoming value. Might be null. + * @return the effective curationDataSelector. Must not be null. + */ + protected abstract String determineEffectiveCurationDataSelector(String curationDataSelector); + + /** + * Method which checks if the given effecticeCurationDataSelector actually exists (is defined). The method will be + * called in case that no curations could be found to make sure that this was not due to a wrong value given for the + * effectiveCurationDataSelector. + *

        + * To be implemented by concrete subclasses. + * + * @param effectiveCurationDataSelector the value to check for existence + * @throws ComponentInfoAdapterException in case that there was any unforeseen exception when trying to check the + * existence of the curationDataSelector. + * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException in case that the given + * effectiveCurationDataSelector does not exist + */ + protected abstract void assureCurationDataSelectorAvailable(String effectiveCurationDataSelector) + throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException; + + /** + * Determine if curations should be fetched in a hierarchical manner or only for the concrete version of a component. + * The default implementation returns true. Subclasses might override this if they do not support + * hierarchies or want to make this configurable. + * + * @return true if the hierarchy should be evaluated, false otherwise, + */ + protected boolean isHierarchyEvaluation() { + + return true; + } + + /** + * Merges the list of found ComponentInfoCuration objects into a resulting curation. + * + * @param data the list of curation (framents) + * @return the resulting curation object + */ + private ComponentInfoCuration mergeCurationData(List data) { + + ComponentInfoCuration mergedCuration = null; + for (ComponentInfoCuration oneCurationData : data) { + mergedCuration = CurationUtil.merge(mergedCuration, oneCurationData); + } + + return mergedCuration; + } + + /** + * Iterates though the hierarchy (optionally) and fetches the curation data objects/fragments via + * {@link #fetchCurationFromRepository(String, String)}. Returns them as a list with the most specific ones at the + * end. + * + * @param effectiveCurationDataSelector the effective curationDataSelector + * @param pathFromPackageUrl the path as determined from the PackageURL (see + * {@link PackageURLHandler#pathFor(String)}) + * @return the list of found curation objects/fragments. Sorting of list is with increasing priority (curations being + * less specific appear first). + * @throws ComponentInfoAdapterException if there was some unforseen issue when accessing the coration repository + * @throws CurationInvalidException if some curation object was malformed + */ + private List fetchAllDataFromCurationRepository(String effectiveCurationDataSelector, + String pathFromPackageUrl) throws ComponentInfoAdapterException, CurationInvalidException { + + List curationsData = new ArrayList<>(); + + String[] pathElements; + if (isHierarchyEvaluation()) { + pathElements = pathFromPackageUrl.split("/"); + } else { + // only take the complete path - do not go through hierarchy + pathElements = new String[] { pathFromPackageUrl }; + } + + String effectivePath = null; + + for (String element : pathElements) { + if (effectivePath == null) { + effectivePath = element; + } else { + effectivePath = effectivePath + "/" + element; + } + ComponentInfoCuration data = fetchCurationFromRepository(effectiveCurationDataSelector, effectivePath); + if (data != null) { + curationsData.add(data); + LOG.debug("Curations found on path '{}' within curation repo.", effectivePath); + } else { + LOG.debug("No Curations found on path '{}' within curation repo.", effectivePath); + } + } + return curationsData; + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java index 7a964784..41f5bf29 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCurator.java @@ -17,8 +17,9 @@ public interface ComponentInfoCurator { * @param curationDataHandle identifies which source should be used for the curation data. * @return the curated component info or - if no curation are done - the incoming object * @throws ComponentInfoAdapterException if the curation could not be read + * @throws CurationInvalidException if the curation data is not valid */ ComponentInfo curate(ComponentInfo componentInfo, CurationDataHandle curationDataHandle) - throws ComponentInfoAdapterException; + throws ComponentInfoAdapterException, CurationInvalidException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java index a8f47574..59474c68 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/ComponentInfoCuratorImpl.java @@ -52,17 +52,17 @@ public ComponentInfoCuratorImpl(CurationProvider curationProvider, * @param curationDataHandle identifies which source should be used for the curation data. * @return the curated component info * @throws ComponentInfoAdapterException if the curation could not be read + * @throws CurationInvalidException if the curation data is not valid */ @Override public ComponentInfo curate(ComponentInfo componentInfo, CurationDataHandle curationDataHandle) - throws ComponentInfoAdapterException { + throws ComponentInfoAdapterException, CurationInvalidException { ComponentInfoCuration foundCuration = this.curationProvider.findCurations(componentInfo.getPackageUrl(), curationDataHandle); if (foundCuration != null) { DefaultComponentInfoImpl componentInfoImpl = new DefaultComponentInfoImpl(componentInfo); applyFoundCurations(componentInfoImpl, foundCuration); - componentInfoImpl.setDataStatus(DataStatusValue.CURATED); return componentInfoImpl; } else { return componentInfo; @@ -76,9 +76,11 @@ private void applyFoundCurations(DefaultComponentInfoImpl componentInfo, Compone for (String copyright : curation.getCopyrights()) { componentInfo.getComponentInfoData().addCopyright(copyright); } + componentInfo.setDataStatus(DataStatusValue.CURATED); } if (curation.getUrl() != null) { componentInfo.getComponentInfoData().setSourceRepoUrl(curation.getUrl()); + componentInfo.setDataStatus(DataStatusValue.CURATED); } if (curation.getLicenses() != null) { componentInfo.getComponentInfoData().clearLicenses(); @@ -95,6 +97,7 @@ private void applyFoundCurations(DefaultComponentInfoImpl componentInfo, Compone licenseInfo.setGivenLicenseText(givenLicenseText); componentInfo.getComponentInfoData().addLicense(licenseInfo); } + componentInfo.setDataStatus(DataStatusValue.CURATED); } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java index 30b84db5..1d07779f 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CuratingComponentInfoAdapter.java @@ -55,10 +55,11 @@ public CuratingComponentInfoAdapter(FilteredComponentInfoProvider filteredCompon * @return the data derived from the scancode results after applying any defined curation. * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be return in such a case. + * @throws CurationInvalidException if the curation data is not valid */ @Override public ComponentInfo getComponentInfo(String packageUrl, CurationDataHandle curationDataHandle) - throws ComponentInfoAdapterException { + throws ComponentInfoAdapterException, CurationInvalidException { if (isFeatureActive()) { diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationInvalidException.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationInvalidException.java new file mode 100644 index 00000000..c1093414 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationInvalidException.java @@ -0,0 +1,28 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +/** + * Exception which indicates that curation data is invalid. + */ +public class CurationInvalidException extends Exception { + + /** + * The constructor. + * + * @param message the message + */ + public CurationInvalidException(String message) { + + super(message); + } + + /** + * The constructor. + * + * @param cause the underlying cause + */ + public CurationInvalidException(Throwable cause) { + + super("Curation data is invalid", cause); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java index 29880a93..ff3dabaf 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationProvider.java @@ -20,8 +20,10 @@ public interface CurationProvider { * @throws ComponentInfoAdapterException if something unexpected happens * @throws ComponentInfoAdapterNonExistingCurationDataSelectorException if the specified curationDataSelector could * not be resolved + * @throws CurationInvalidException if the curation data is not valid */ ComponentInfoCuration findCurations(String packageUrl, CurationDataHandle curationDataHandle) - throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException; + throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException, + CurationInvalidException; } \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtil.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtil.java new file mode 100644 index 00000000..84df479a --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtil.java @@ -0,0 +1,110 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import java.util.ArrayList; +import java.util.List; + +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; + +/** + * Helper methods for working with curation data. + */ +public class CurationUtil { + + private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + + /** + * Parses curation data from a YAML string into a ComponentInfoCuration object. + * + * @param curationYaml The YAML data to be parsed. + * @return A ComponentInfoCuration object containing parsed curation data. + * @throws CurationInvalidException If any errors occur during YAML parsing. + */ + public static ComponentInfoCuration curationDataFromString(String curationYaml) throws CurationInvalidException { + + ComponentInfoCuration result; + try { + // Use Jackson YAML mapper to parse YAML into an object + result = yamlMapper.readValue(curationYaml, ComponentInfoCuration.class); + } catch (JsonProcessingException e) { + throw new CurationInvalidException(e); + } + if (result.getName() == null || result.getName().isEmpty()) { + throw new CurationInvalidException("The name attribute of the curation data must not be null or empty"); + } + return result; + } + + /** + * Private constructor prohibits instantiation + */ + private CurationUtil() { + + } + + /** + * Merges two ComponentInfoCuration objects. The "second" argument represents the more specific one (with higher + * priority): name and url of the second one supersede the first one. If data is concatenated as list, then the + * elements from the second are are front of the list, the elements from the first are at the back. + * + * @param first the first object (less spoecific / lower priority) + * @param second the second object (more specific / higher priority) + * @return the merged result + */ + @SuppressWarnings("unchecked") + public static ComponentInfoCuration merge(ComponentInfoCuration first, ComponentInfoCuration second) { + + if (first == null) { + return second; + } + if (second == null) { + return first; + } + ComponentInfoCuration merged = new ComponentInfoCuration(); + merged.setName(second.getName() != null ? second.getName() : first.getName()); // latest wins + merged.setNote(join(" / ", second.getNote(), first.getNote())); + merged.setUrl(second.getUrl() != null ? second.getUrl() : first.getUrl()); // latest wins + merged.setCopyrights(join(second.getCopyrights(), first.getCopyrights())); + merged.setLicenses(join(second.getLicenses(), first.getLicenses())); + merged.setExcludedPaths(join(second.getExcludedPaths(), first.getExcludedPaths())); + merged.setLicenseCurations(join(second.getLicenseCurations(), first.getLicenseCurations())); + merged.setCopyrightCurations(join(second.getCopyrightCurations(), first.getCopyrightCurations())); + return merged; + } + + private static String join(String delimiter, String arg1, String arg2) { + + if (arg1 == null && arg2 == null) { + return null; + } + if (arg1 != null) { + StringBuffer sb = new StringBuffer(arg1); + if (arg2 != null) { + sb.append(delimiter).append(arg2); + } + return sb.toString(); + } else { + return arg2; + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private static List join(List arg1, List arg2) { + + if (arg1 == null && arg2 == null) { + return null; + } + if (arg1 != null) { + List result = new ArrayList(arg1); + if (arg2 != null) { + result.addAll(arg2); + } + return result; + } else { + return arg2; + } + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java index b72fd9ab..a9f38d29 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java @@ -14,8 +14,6 @@ import com.devonfw.tools.solicitor.common.LogMessages; import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; -import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; -import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; import com.devonfw.tools.solicitor.componentinfo.curation.model.CurationList; import com.fasterxml.jackson.databind.ObjectMapper; @@ -27,7 +25,7 @@ * */ @Component -public class SingleFileCurationProvider implements CurationProvider { +public class SingleFileCurationProvider extends AbstractHierarchicalCurationProvider { private static final Logger LOG = LoggerFactory.getLogger(SingleFileCurationProvider.class); private static final ObjectMapper yamlMapper; @@ -41,8 +39,6 @@ public class SingleFileCurationProvider implements CurationProvider { private boolean curationsExistenceLogged; - private AllKindsPackageURLHandler packageURLHandler; - /** * The constructor. * @@ -51,31 +47,25 @@ public class SingleFileCurationProvider implements CurationProvider { @Autowired public SingleFileCurationProvider(AllKindsPackageURLHandler packageURLHandler) { - this.packageURLHandler = packageURLHandler; + super(packageURLHandler); } /** - * Return the curation data for a given package. + * Sets curationsFileName. * - * @param packageUrl identifies the package - * @param curationDataHandle identifies which source should be used for the curation data. - * @return the curation data if it exists. null if no curation exist for the package or the - * curationDataSelector was given as "none". - * @throws ComponentInfoAdapterException if something unexpected happens + * @param curationsFileName new value of curationsFileName. */ + @Value("${solicitor.scancode.curations-filename}") + public void setCurationsFileName(String curationsFileName) { + + this.curationsFileName = curationsFileName; + } + @Override - public ComponentInfoCuration findCurations(String packageUrl, CurationDataHandle curationDataHandle) - throws ComponentInfoAdapterException { - - // SelectorCurationDataHandle is the only implementation supported here. - String curationDataSelector = ((SelectorCurationDataHandle) curationDataHandle).getCurationDataSelector(); - // Return null if curationDataSelector is "none" - if ("none".equalsIgnoreCase(curationDataSelector)) { - return null; - } - ComponentInfoCuration foundCuration = null; + protected ComponentInfoCuration fetchCurationFromRepository(String effectiveCurationDataSelector, + String pathFragmentWithinRepo) throws ComponentInfoAdapterException, CurationInvalidException { - String packagePathPart = this.packageURLHandler.pathFor(packageUrl); + ComponentInfoCuration foundCuration = null; File curationsFile = new File(this.curationsFileName); if (!curationsFile.exists()) { @@ -96,7 +86,7 @@ public ComponentInfoCuration findCurations(String packageUrl, CurationDataHandle for (ComponentInfoCuration curation : curationList.getArtifacts()) { String component = curation.getName(); - if (component.equals(packagePathPart)) { + if (component.equals(pathFragmentWithinRepo)) { foundCuration = curation; break; } @@ -109,15 +99,26 @@ public ComponentInfoCuration findCurations(String packageUrl, CurationDataHandle return foundCuration; } - /** - * Sets curationsFileName. - * - * @param curationsFileName new value of curationsFileName. - */ - @Value("${solicitor.scancode.curations-filename}") - public void setCurationsFileName(String curationsFileName) { + @Override + protected void validateEffectiveCurationDataSelector(String effectiveCurationDataSelector) + throws ComponentInfoAdapterNonExistingCurationDataSelectorException { + + // as the curationDataSelector is not supported/used there is nothing to do here + } + + @Override + protected String determineEffectiveCurationDataSelector(String curationDataSelector) { + + // actually this value is unused in the class + return "-"; + } + + @Override + protected void assureCurationDataSelectorAvailable(String effectiveCurationDataSelector) + throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException { + + // as the curationDataSelector is not supported/used there is nothing to do here - this.curationsFileName = curationsFileName; } } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java index 94e85453..7ccd7a9e 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/ComponentInfoCuration.java @@ -2,6 +2,7 @@ import java.util.List; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; import com.fasterxml.jackson.annotation.JsonInclude; /** @@ -23,6 +24,10 @@ public class ComponentInfoCuration { private List excludedPaths; + private List licenseCurations; + + private List copyrightCurations; + /** * The constructor. */ @@ -126,4 +131,56 @@ public void setExcludedPaths(List excludedPaths) { this.excludedPaths = excludedPaths; } + /** + * @return licenseCurations + */ + public List getLicenseCurations() { + + return this.licenseCurations; + } + + /** + * @param licenseCurations new value of {@link #getLicenseCurations}. + */ + public void setLicenseCurations(List licenseCurations) { + + this.licenseCurations = licenseCurations; + } + + /** + * @return copyrightCurations + */ + public List getCopyrightCurations() { + + return this.copyrightCurations; + } + + /** + * @param copyrightCurations new value of {@link #getCopyrightCurations}. + */ + public void setCopyrightCurations(List copyrightCurations) { + + this.copyrightCurations = copyrightCurations; + } + + /** + * Validates the curation data. + * + * @throws CurationInvalidException indicates that the curation data is not valid. + */ + public void validate() throws CurationInvalidException { + + if (this.licenseCurations != null) { + for (LicenseCuration lc : this.licenseCurations) { + lc.validate(); + } + } + if (this.copyrightCurations != null) { + for (CopyrightCuration cc : this.copyrightCurations) { + cc.validate(); + } + } + + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CopyrightCuration.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CopyrightCuration.java new file mode 100644 index 00000000..b4d2e9ee --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CopyrightCuration.java @@ -0,0 +1,214 @@ +package com.devonfw.tools.solicitor.componentinfo.curation.model; + +import java.util.regex.Pattern; + +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Copyright curation. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class CopyrightCuration { + + private CurationOperation operation; + + private Pattern path; + + private Pattern oldCopyright; + + private String newCopyright; + + private String comment; + + /** + * The constructor. + */ + public CopyrightCuration() { + + } + + /** + * @return operation + */ + public CurationOperation getOperation() { + + return this.operation; + } + + /** + * Returns the regular expressing which specifies the file path(s) to which this curation should apply. + * + * @return path + */ + public String getPath() { + + return this.path.toString(); + } + + /** + * Returns the regular expressing which specifies the copyright(s) to which this curation should apply. + * + * @return copyright + */ + public String getOldCopyright() { + + return this.oldCopyright.toString(); + } + + /** + * @return newCopyright + */ + public String getNewCopyright() { + + return this.newCopyright; + } + + /** + * Returns the + * + * @return comment + */ + public String getComment() { + + return this.comment; + } + + /** + * @param operation new value of {@link #getOperation}. + */ + public void setOperation(CurationOperation operation) { + + this.operation = operation; + } + + /** + * Sets the regular expression for specifying the file paths(s) to which this curation should apply. + * + * @param path new value of {@link #getPath}. + */ + public void setPath(String path) { + + this.path = Pattern.compile(path); + } + + /** + * Sets the regular expression for specifying the copyright(s) to which this curation should apply. + * + * @param oldCopyright new value of {@link #getOldCopyright}. + */ + public void setOldCopyright(String oldCopyright) { + + this.oldCopyright = Pattern.compile(oldCopyright, Pattern.DOTALL); + } + + /** + * @param newCopyright new value of {@link #getNewCopyright}. + */ + public void setNewCopyright(String newCopyright) { + + this.newCopyright = newCopyright; + } + + /** + * Sets the comment describing the curation + * + * @param comment new value of {@link #getComment}. + */ + public void setComment(String comment) { + + this.comment = comment; + } + + /** + * Check if this curation matches the given data. If any of the conditions are not set (via the appropriate setters or + * by reading the data from yaml) then the condition will be ignored. + * + * @param path the path to check + * @param copyright the ruleIdentifier to check + * @return true if the conditions match, false otherwise. + */ + @SuppressWarnings("hiding") + public boolean matches(String path, String copyright) { + + if (this.operation != CurationOperation.REMOVE && this.operation != CurationOperation.REPLACE) { + // this method should only match in case of REMOVE or REPLACE + return false; + } + if (this.path != null && (path == null || !this.path.matcher(path).matches())) { + return false; + } + if (this.oldCopyright != null && (copyright == null || !this.oldCopyright.matcher(copyright).matches())) { + return false; + } + return true; + + } + + /** + * Check if this a ADD curation and matches the given path. If any of the conditions are not set (via the appropriate + * setters or by reading the data from yaml) then the condition will be ignored. + * + * @param path the path to check + * @return true if this is an ADD curation and the conditions match, false otherwise. + */ + @SuppressWarnings("hiding") + public boolean matches(String path) { + + if (this.operation != CurationOperation.ADD) { + // no match if it is not an ADD curation + return false; + } + + if (this.path == null) { + // if path condition is not set then this will only match if being called with null argument. + return path == null; + } else { + if (path == null) { + return false; + } else { + // if path condition is set the check if the given path matches the Pattern + return this.path.matcher(path).matches(); + } + } + + } + + /** + * Validates if the data of this object is consistent. + * + * @throws CurationInvalidException if the object is invalid + */ + public void validate() throws CurationInvalidException { + + if (this.operation == null) { + throw new CurationInvalidException("Operation must not be null for copyright curation"); + } + if (this.operation == CurationOperation.REMOVE || this.operation == CurationOperation.REPLACE) { + if (this.path == null && this.oldCopyright == null) { + throw new CurationInvalidException( + "For REMOVE/REPLACE copyright curation at least one condition must be defined"); + } + } + if (this.operation == CurationOperation.REMOVE) { + if (this.newCopyright != null) { + throw new CurationInvalidException("For REMOVE copyright curation the newCopyright must not be set"); + } + } + if (this.operation == CurationOperation.REPLACE) { + if (this.newCopyright == null) { + throw new CurationInvalidException("For REPLACE copyright curation newCopyright must be set"); + } + } + if (this.operation == CurationOperation.ADD) { + if (this.oldCopyright != null) { + throw new CurationInvalidException("For ADD copyright curation oldCopyright must not be defined"); + } + if (this.newCopyright == null) { + throw new CurationInvalidException("For ADD copyright curation newCopyright must be set"); + } + } + + } + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CurationOperation.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CurationOperation.java new file mode 100644 index 00000000..e55aaa02 --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CurationOperation.java @@ -0,0 +1,20 @@ +package com.devonfw.tools.solicitor.componentinfo.curation.model; + +/** + * The curation operation to be performed. + */ +public enum CurationOperation { + /** + * Remove / Ignore a license or copyright finding. + */ + REMOVE, + /** + * Replace a license or copyright finding with a different one. + */ + REPLACE, + /** + * Add a license or copyright. + */ + ADD + +} diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseCuration.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseCuration.java new file mode 100644 index 00000000..8fec7eec --- /dev/null +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseCuration.java @@ -0,0 +1,320 @@ +package com.devonfw.tools.solicitor.componentinfo.curation.model; + +import java.util.regex.Pattern; + +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * License finding curation. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class LicenseCuration { + + /** + * Interface data structure for new license data (for ADD or REPLACE). + */ + public static class NewLicenseData { + /** + * new license id + */ + public String license; + + /** + * new url pointing to license text + */ + public String url; + } + + private CurationOperation operation; + + private Pattern path; + + private Pattern ruleIdentifier; + + private Pattern matchedText; + + private Pattern oldLicense; + + private String newLicense; + + private String url; + + private String comment; + + /** + * The constructor. + */ + public LicenseCuration() { + + } + + /** + * @return operation + */ + public CurationOperation getOperation() { + + return this.operation; + } + + /** + * Returns the regular expressing which specifies the file path(s) to which this curation should apply. + * + * @return path + */ + public String getPath() { + + return this.path.toString(); + } + + /** + * Returns the regular expressing which specifies the rule identifiers(s) to which this curation should apply. + * + * @return ruleIdentifier + */ + public String getRuleIdentifier() { + + return this.ruleIdentifier.toString(); + } + + /** + * Returns the regular expressing which specifies the matched text(s) to which this curation should apply. + * + * @return matchedText + */ + public String getMatchedText() { + + return this.matchedText.toString(); + } + + /** + * Returns the regular expressing which specifies the old license (id) to which this curation should apply. + * + * @return new oldLicense + */ + public String getOldLicense() { + + return this.oldLicense.toString(); + } + + /** + * @return new newLicense + */ + public String getNewLicense() { + + return this.newLicense; + } + + /** + * @return url + */ + public String getUrl() { + + return this.url; + } + + /** + * Returns the + * + * @return comment + */ + public String getComment() { + + return this.comment; + } + + /** + * @param operation new value of {@link #getOperation}. + */ + public void setOperation(CurationOperation operation) { + + this.operation = operation; + } + + /** + * Sets the regular expression for specifying the file paths(s) to which this curation should apply. + * + * @param path new value of {@link #getPath}. + */ + public void setPath(String path) { + + this.path = Pattern.compile(path); + } + + /** + * Sets the regular expression for specifying the rule identifier(s) to which this curation should apply + * + * @param ruleIdentifier new value of {@link #getRuleIdentifier}. + */ + public void setRuleIdentifier(String ruleIdentifier) { + + this.ruleIdentifier = Pattern.compile(ruleIdentifier); + } + + /** + * Sets the regular expression for specifying the matched text(s) to which this curation should apply + * + * @param matchedText new value of {@link #getMatchedText}. + */ + public void setMatchedText(String matchedText) { + + this.matchedText = Pattern.compile(matchedText, Pattern.DOTALL); + } + + /** + * Sets the regular expression for specifying the old license (id) to which this curation should apply + * + * @param oldLicense new value of {@link #getOldLicense}. + */ + public void setOldLicense(String oldLicense) { + + this.oldLicense = Pattern.compile(oldLicense, Pattern.DOTALL); + } + + /** + * @param newLicense new value of {@link #getNewLicense}. + */ + public void setNewLicense(String newLicense) { + + this.newLicense = newLicense; + } + + /** + * @param url new value of {@link #getUrl}. + */ + public void setUrl(String url) { + + this.url = url; + } + + /** + * Sets the comment describing the curation + * + * @param comment new value of {@link #getComment}. + */ + public void setComment(String comment) { + + this.comment = comment; + } + + /** + * Check if this is a REMOVE or REPLACE curation and matches the given data. If any of the conditions are not set (via + * the appropriate setters or by reading the data from yaml) then the condition will be ignored. + * + * @param path the path to check + * @param ruleIdentifier the ruleIdentifier to check + * @param matchedText the matchedText to check + * @param oldLicense the old license (as produced by the scancode rule) to check + * @return true if this is a REMOVE or REPLACE curation and the conditions match, false + * otherwise. + */ + @SuppressWarnings("hiding") + public boolean matches(String path, String ruleIdentifier, String matchedText, String oldLicense) { + + if (this.operation != CurationOperation.REMOVE && this.operation != CurationOperation.REPLACE) { + return false; + } + + if (this.path != null && (path == null || !this.path.matcher(path).matches())) { + return false; + } + if (this.ruleIdentifier != null + && (ruleIdentifier == null || !this.ruleIdentifier.matcher(ruleIdentifier).matches())) { + return false; + } + if (this.matchedText != null && (matchedText == null || !this.matchedText.matcher(matchedText).matches())) { + return false; + } + if (this.oldLicense != null && (oldLicense == null || !this.oldLicense.matcher(oldLicense).matches())) { + return false; + } + return true; + + } + + /** + * Check if this is a ADD curation and matches the given path. If any of the conditions are not set (via the appropriate + * setters or by reading the data from yaml) then the condition will be ignored. + * + * @param path the path to check + * @return true if this is a ADD curation and the conditions match, false otherwise. + */ + @SuppressWarnings("hiding") + public boolean matches(String path) { + + if (this.operation != CurationOperation.ADD) { + // no match if it is not an ADD curation + return false; + } + + if (this.path == null) { + // if path condition is not set then this will only match if being called with null argument. + return path == null; + } else { + if (path == null) { + return false; + } + // if path condition is set the check if the given path matches the Pattern + return this.path.matcher(path).matches(); + } + + } + + /** + * Returns the new license data to be applied in case that the curation rule matches. + * + * @return the new license data. null indicates that the found license should be removed (REMOVE + * operation). If the data fields license or url contain null this indicates that they should + * remain unchanged. (Only applicable for REPLACE operation) + */ + public NewLicenseData newLicenseData() { + + if (this.operation == CurationOperation.REMOVE) { + // in case of REMOVE indicate this by returning null + return null; + } + NewLicenseData result = new NewLicenseData(); + result.license = this.newLicense; + result.url = this.url; + return result; + } + + /** + * Validates if the data of this object is consistent. + * + * @throws CurationInvalidException if the object is invalid + */ + public void validate() throws CurationInvalidException { + + if (this.operation == null) { + throw new CurationInvalidException("Operation must not be null for license curation"); + } + if (this.operation == CurationOperation.REMOVE || this.operation == CurationOperation.REPLACE) { + if (this.path == null && this.ruleIdentifier == null && this.matchedText == null && this.oldLicense == null) { + throw new CurationInvalidException( + "For REMOVE/REPLACE license curation at least one condition must be defined"); + } + } + if (this.operation == CurationOperation.REMOVE) { + if (this.newLicense != null || this.url != null) { + throw new CurationInvalidException("For REMOVE license curation neither newLicense nor url must be set"); + } + } + if (this.operation == CurationOperation.REPLACE) { + if (this.newLicense == null && this.url == null) { + throw new CurationInvalidException("For REPLACE license curation at least license or url must be set"); + } + } + if (this.operation == CurationOperation.ADD) { + if (this.ruleIdentifier != null || this.matchedText != null || this.oldLicense != null) { + throw new CurationInvalidException( + "For ADD license curation at neither ruleIdentifier nor matchedText nor oldLicense must be defined"); + } + if (this.newLicense == null || this.url == null) { + throw new CurationInvalidException("For ADD license curation license and url must be set"); + } + } + + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java index 8102cdd5..5765b383 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProvider.java @@ -17,9 +17,13 @@ import com.devonfw.tools.solicitor.componentinfo.CurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.DataStatusValue; import com.devonfw.tools.solicitor.componentinfo.DefaultComponentInfoImpl; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; import com.devonfw.tools.solicitor.componentinfo.curation.CurationProvider; import com.devonfw.tools.solicitor.componentinfo.curation.FilteredComponentInfoProvider; import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.CurationOperation; +import com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration; import com.devonfw.tools.solicitor.componentinfo.scancode.ScancodeComponentInfo.ScancodeComponentInfoData; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -44,8 +48,6 @@ public class FilteredScancodeComponentInfoProvider implements FilteredComponentI private double licenseToTextRatioToTakeCompleteFile = 90; - private AllKindsPackageURLHandler packageURLHandler; - private ScancodeRawComponentInfoProvider fileScancodeRawComponentInfoProvider; private CurationProvider curationProvider; @@ -63,7 +65,6 @@ public FilteredScancodeComponentInfoProvider(ScancodeRawComponentInfoProvider fi AllKindsPackageURLHandler packageURLHandler, CurationProvider curationProvider) { this.fileScancodeRawComponentInfoProvider = fileScancodeRawComponentInfoProvider; - this.packageURLHandler = packageURLHandler; this.curationProvider = curationProvider; } @@ -98,10 +99,11 @@ public void setMinLicensefileNumberOfLines(int minLicensefileNumberOfLines) { * @return the read scancode information * @throws ComponentInfoAdapterException if there was an exception when reading the data. In case that there is no * data available no exception will be thrown. Instead null will be returned in such a case. + * @throws CurationInvalidException if the curation data is not valid */ @Override public ComponentInfo getComponentInfo(String packageUrl, CurationDataHandle curationDataHandle) - throws ComponentInfoAdapterException { + throws ComponentInfoAdapterException, CurationInvalidException { ScancodeRawComponentInfo rawScancodeData; try { @@ -143,14 +145,15 @@ private void addSupplementedData(ScancodeRawComponentInfo rawScancodeData, * @param curationDataHandle identifies which source should be used for the curation data. * @return the ScancodeComponentInfo * @throws ComponentInfoAdapterException if there was an issue during parsing + * @throws CurationInvalidException if the curation data is not valid */ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, ScancodeRawComponentInfo rawScancodeData, - CurationDataHandle curationDataHandle) throws ComponentInfoAdapterException { + CurationDataHandle curationDataHandle) throws ComponentInfoAdapterException, CurationInvalidException { ScancodeComponentInfo componentScancodeInfos = new ScancodeComponentInfo(this.minLicenseScore, this.minLicensefileNumberOfLines); componentScancodeInfos.setPackageUrl(packageUrl); - // set status to NO_ISSUES. This might be overridden later if issues are detected + // set status to NO_ISSUES. This might be overridden later if issues are detected or curations are applied componentScancodeInfos.setDataStatus(DataStatusValue.NO_ISSUES); // get the object which hold the actual data @@ -161,8 +164,12 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod // Get all excludedPaths in this curation List excludedPaths = null; + List licenseCurations = null; + List copyrightCurations = null; if (componentInfoCuration != null) { excludedPaths = componentInfoCuration.getExcludedPaths(); + licenseCurations = componentInfoCuration.getLicenseCurations(); + copyrightCurations = componentInfoCuration.getCopyrightCurations(); } JsonNode scancodeJson; @@ -176,6 +183,8 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod for (JsonNode file : scancodeJson.get("files")) { String path = file.get("path").asText(); if (isExcluded(path, excludedPaths)) { + // this is a curation operation, so set the status + componentScancodeInfos.setDataStatus(DataStatusValue.CURATED); continue; } if ("directory".equals(file.get("type").asText())) { @@ -188,10 +197,25 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod double licenseTextRatio = file.get("percentage_of_license_text").asDouble(); boolean takeCompleteFile = licenseTextRatio >= this.licenseToTextRatioToTakeCompleteFile; for (JsonNode cr : file.get("copyrights")) { + String copyright; if (cr.has("copyright")) { - scancodeComponentInfoData.addCopyright(cr.get("copyright").asText()); + copyright = cr.get("copyright").asText(); } else { - scancodeComponentInfoData.addCopyright(cr.get("value").asText()); + copyright = cr.get("value").asText(); + } + String copyrightAfterCuration = getEffectiveCopyrightWithCuration(path, copyright, copyrightCurations); + if (copyrightAfterCuration != null) { + if (!copyrightAfterCuration.equals(copyright)) { + // the copyright info changed due to applying a curation, so set the status + componentScancodeInfos.setDataStatus(DataStatusValue.CURATED); + } + scancodeComponentInfoData.addCopyright(copyrightAfterCuration); + } else { + if (copyright != null) { + // the copyright info was removed due to applying a curation, so set the status + componentScancodeInfos.setDataStatus(DataStatusValue.CURATED); + + } } } @@ -200,7 +224,13 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod boolean classPathExceptionExists = false; int numberOfGplLicenses = 0; for (JsonNode li : file.get("licenses")) { - String licenseName = li.get("spdx_license_key").asText(); + LicenseCuration.NewLicenseData effective = getEffectiveLicenseInfoWithCuration(path, li, licenseCurations); + if (effective == null) { + // license finding to be REMOVED via finding + continue; + } + String licenseName = effective.license != null ? effective.license : li.get("spdx_license_key").asText(); + if ("Classpath-exception-2.0".equals(licenseName)) { classPathExceptionExists = true; } @@ -228,19 +258,29 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod } } for (JsonNode li : file.get("licenses")) { - String licenseid = li.get("key").asText(); - String licenseName = li.get("spdx_license_key").asText(); + LicenseCuration.NewLicenseData effective = getEffectiveLicenseInfoWithCuration(path, li, licenseCurations); + if (effective == null) { + // license finding to be REMOVED via finding + // this is a curation operation, so set the status + componentScancodeInfos.setDataStatus(DataStatusValue.CURATED); + continue; + } + if (effective.license != null || effective.url != null) { + // license or url are altered due to curation, so set the status + componentScancodeInfos.setDataStatus(DataStatusValue.CURATED); + } + String licenseName = effective.license != null ? effective.license : li.get("spdx_license_key").asText(); String effectiveLicenseName = spdxIdMap.get(licenseName); if (effectiveLicenseName == null) { // not contained in map --> this must be the Classpath-exception-2.0 continue; } else { licenseName = effectiveLicenseName; - if (licenseName.endsWith("WITH Classpath-exception-2.0")) { - licenseid = licenseid + "WITH Classpath-exception-2.0"; - } } String licenseDefaultUrl = li.get("scancode_text_url").asText(); + if (effective.url != null) { + licenseDefaultUrl = effective.url; + } licenseDefaultUrl = normalizeLicenseUrl(packageUrl, licenseDefaultUrl); double score = li.get("score").asDouble(); String licenseUrl = path; @@ -252,6 +292,13 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod licenseUrl += "-L" + endLine; } } + if (effective.url != null) { + // curation redefined the license URL + licenseUrl = effective.url; + // enforce that the filescore always exceeds the threshold + startLine = 0; + endLine = Integer.MAX_VALUE; + } licenseUrl = normalizeLicenseUrl(packageUrl, licenseUrl); String givenLicenseText = null; @@ -259,10 +306,18 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod givenLicenseText = this.fileScancodeRawComponentInfoProvider.retrieveContent(packageUrl, licenseUrl); } - scancodeComponentInfoData.addLicense(licenseid, licenseName, licenseDefaultUrl, score, licenseUrl, + scancodeComponentInfoData.addLicense(licenseName, licenseName, licenseDefaultUrl, score, licenseUrl, givenLicenseText, endLine - startLine); } + // do any per scanned file postprocessing + addCopyrightsByCuration(path, copyrightCurations, componentScancodeInfos); + addLicensesByCuration(packageUrl, path, licenseCurations, componentScancodeInfos); + } + // add copyrights / licenses due to curations on package level + addCopyrightsByCuration(null, copyrightCurations, componentScancodeInfos); + addLicensesByCuration(packageUrl, null, licenseCurations, componentScancodeInfos); + if (scancodeComponentInfoData.getNoticeFileUrl() != null) { scancodeComponentInfoData.setNoticeFileContent(this.fileScancodeRawComponentInfoProvider .retrieveContent(packageUrl, scancodeComponentInfoData.getNoticeFileUrl())); @@ -270,6 +325,155 @@ private ScancodeComponentInfo parseAndMapScancodeJson(String packageUrl, Scancod return componentScancodeInfos; } + /** + * Gets the effective license info after possibly applying curations for a single license finding. + * + * @param path + * @param li + * @param licenseCurations + * @return + */ + private LicenseCuration.NewLicenseData getEffectiveLicenseInfoWithCuration(String path, JsonNode li, + List licenseCurations) { + + if (licenseCurations == null) { + // NewLicenseData with all members being null indicates: no change + return new LicenseCuration.NewLicenseData(); + } + + String ruleIdentifier = li.get("matched_rule").get("identifier").asText(); + String matchedText = li.get("matched_text").asText(); + String spdxId = li.get("spdx_license_key").asText(); + + for (LicenseCuration rule : licenseCurations) { + if (rule.matches(path, ruleIdentifier, matchedText, spdxId)) { + LicenseCuration.NewLicenseData result = rule.newLicenseData(); + if (LOG.isDebugEnabled()) { + if (result == null) { + LOG.debug("License finding of rule '{}' in '{}' will be ignored due to remove license curation", + ruleIdentifier, path); + } else { + LOG.debug("License finding of rule '{}' in '{}' will be replaced by SPDX-ID '{}' and URL '{}'", + ruleIdentifier, path, result.license, result.url); + + } + } + return result; + } + } + // NewLicenseData with all members being null indicates: no change + return new LicenseCuration.NewLicenseData(); + } + + /** + * Adds license entries due to curations. + * + * @param packageUrl + * @param path + * @param licenseCurations + * @param componentScancodeInfos + */ + private void addLicensesByCuration(String packageUrl, String path, List licenseCurations, + ScancodeComponentInfo componentScancodeInfos) { + + if (licenseCurations == null) { + // no curations available: return empty collection + return; + } + + for (LicenseCuration rule : licenseCurations) { + if (rule.matches(path)) { + if (rule.getOperation() == CurationOperation.ADD) { + LicenseCuration.NewLicenseData license = rule.newLicenseData(); + if (LOG.isDebugEnabled()) { + LOG.debug( + "License finding with SPDX-ID '{}' and url '{}' in '{}' will be added due to ADD copyright curation", + license.license, license.url, path); + } + String licenseUrl = normalizeLicenseUrl(packageUrl, license.url); + String givenLicenseText = this.fileScancodeRawComponentInfoProvider.retrieveContent(packageUrl, licenseUrl); + + componentScancodeInfos.getComponentInfoData().addLicense(license.license, license.license, license.url, 100, + licenseUrl, givenLicenseText, Integer.MAX_VALUE); + componentScancodeInfos.setDataStatus(DataStatusValue.CURATED); + } else { + throw new IllegalStateException("This seems to be a bug"); + } + } + } + } + + /** + * Gets the effective copyright after possibly applying curations for a single copyright finding. + * + * @param path + * @param copyright + * @param copyrightCurations + * @return + */ + private String getEffectiveCopyrightWithCuration(String path, String copyright, + List copyrightCurations) { + + if (copyrightCurations == null) { + // no curations available: return the original copyright + return copyright; + } + + for (CopyrightCuration rule : copyrightCurations) { + if (rule.matches(path, copyright)) { + if (rule.getOperation() == CurationOperation.REMOVE) { + if (LOG.isDebugEnabled()) { + LOG.debug("Copyright finding '{}' in '{}' will be ignored due to remove copyright curation", copyright, + path); + } + return null; + } + if (rule.getOperation() == CurationOperation.REPLACE) { + if (LOG.isDebugEnabled()) { + LOG.debug("Copyright finding '{}' in '{}' will be ignored due to remove copyright curation", copyright, + path); + } + return rule.getNewCopyright(); + } + throw new IllegalStateException("This seems to be a bug"); + } + } + // no curations applied: return the original copyright + return copyright; + } + + /** + * Adds copyrights entries due to curations. + * + * @param path + * @param copyrightCurations + * @param componentScancodeInfos + */ + private void addCopyrightsByCuration(String path, List copyrightCurations, + ScancodeComponentInfo componentScancodeInfos) { + + if (copyrightCurations == null) { + // no curations available: return empty collection + return; + } + + for (CopyrightCuration rule : copyrightCurations) { + if (rule.matches(path)) { + if (rule.getOperation() == CurationOperation.ADD) { + String copyrightToBeAdded = rule.getNewCopyright(); + if (LOG.isDebugEnabled()) { + LOG.debug("Copyright finding '{}' in '{}' will be added due to ADD copyright curation", copyrightToBeAdded, + path); + } + componentScancodeInfos.getComponentInfoData().addCopyright(copyrightToBeAdded); + componentScancodeInfos.setDataStatus(DataStatusValue.CURATED); + } else { + throw new IllegalStateException("This seems to be a bug"); + } + } + } + } + /** * Adjustment of license paths/urls so that they might retrieved * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java index c8b8923f..23de13e6 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfo.java @@ -271,7 +271,8 @@ public void addLicense(String licenseId, String licenseName, String licenseDefau String resultingFilePath = existingLicenseInfo.getLicenseUrl(); String resultingGivenText = existingLicenseInfo.getGivenLicenseText(); int resultingFileScore = existingLicenseInfo.getLicenseFileScore(); - if (fileScore > existingLicenseInfo.getLicenseFileScore()) { + if (fileScore > existingLicenseInfo.getLicenseFileScore() && givenLicenseText != null + && !givenLicenseText.isEmpty()) { resultingFilePath = filePath; resultingFileScore = fileScore; resultingGivenText = givenLicenseText; @@ -281,8 +282,10 @@ public void addLicense(String licenseId, String licenseName, String licenseDefau } else { if (score >= this.minLicenseScore) { - this.licenses.put(licenseId, new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, - filePath, givenLicenseText, fileScore, this.minLicensefileNumberOfLines)); + this.licenses.put(licenseId, + new ScancodeLicenseInfo(licenseId, licenseName, licenseDefaultUrl, score, filePath, givenLicenseText, + (givenLicenseText != null && !givenLicenseText.isEmpty()) ? fileScore : 0, + this.minLicensefileNumberOfLines)); } } } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProviderTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProviderTest.java new file mode 100644 index 00000000..e78481d5 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProviderTest.java @@ -0,0 +1,191 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.CurationOperation; + +/** + * Tests for {@link AbstractHierarchicalCurationProvider}. + * + */ +class AbstractHierarchicalCurationProviderTest { + + @Test + void testFindCurationsHierarchyOnDefaultCurationDataSelector() + throws ComponentInfoAdapterNonExistingCurationDataSelectorException, ComponentInfoAdapterException, + CurationInvalidException { + + AbstractHierarchicalCurationProvider provider = createObjectUnderTest(true); + + ComponentInfoCuration result = provider.findCurations("pkg:/maven/ch.qos.logback/logback-classic@1.2.3", + new SelectorCurationDataHandle(null)); + assertEquals("pkg/maven/ch/qos/logback/logback-classic/1.2.3", result.getName()); + assertEquals(7, result.getCopyrightCurations().size()); + assertTrue(result.getCopyrightCurations().get(0).getNewCopyright().contains("theDefault")); + assertTrue(result.getCopyrightCurations().get(0).getNewCopyright() + .endsWith("pkg/maven/ch/qos/logback/logback-classic/1.2.3")); + assertTrue(result.getCopyrightCurations().get(6).getNewCopyright().endsWith("pkg")); + + } + + @Test + void testFindCurationsHierarchyOnNonDefaultCurationDataSelector() + throws ComponentInfoAdapterNonExistingCurationDataSelectorException, ComponentInfoAdapterException, + CurationInvalidException { + + AbstractHierarchicalCurationProvider provider = createObjectUnderTest(true); + + ComponentInfoCuration result = provider.findCurations("pkg:/maven/ch.qos.logback/logback-classic@1.2.3", + new SelectorCurationDataHandle("someSelector")); + assertEquals("pkg/maven/ch/qos/logback/logback-classic/1.2.3", result.getName()); + assertEquals(7, result.getCopyrightCurations().size()); + assertTrue(result.getCopyrightCurations().get(0).getNewCopyright().contains("someSelector")); + assertTrue(result.getCopyrightCurations().get(0).getNewCopyright() + .endsWith("pkg/maven/ch/qos/logback/logback-classic/1.2.3")); + assertTrue(result.getCopyrightCurations().get(6).getNewCopyright().endsWith("pkg")); + + } + + @Test + void testFindCurationsHierarchyWhenCurationExistOnlyForVersion() + throws ComponentInfoAdapterNonExistingCurationDataSelectorException, ComponentInfoAdapterException, + CurationInvalidException { + + AbstractHierarchicalCurationProvider provider = createObjectUnderTest(true); + + ComponentInfoCuration result = provider.findCurations("pkg:/maven/ch.qos.logback/logback-classic@1.2.3", + new SelectorCurationDataHandle("curationonlyonversion")); + assertEquals("pkg/maven/ch/qos/logback/logback-classic/1.2.3", result.getName()); + assertEquals(1, result.getCopyrightCurations().size()); + assertTrue(result.getCopyrightCurations().get(0).getNewCopyright().contains("curationonlyonversion")); + assertTrue(result.getCopyrightCurations().get(0).getNewCopyright() + .endsWith("pkg/maven/ch/qos/logback/logback-classic/1.2.3")); + } + + @Test + void testFindCurationsHierarchyDisabled() throws ComponentInfoAdapterNonExistingCurationDataSelectorException, + ComponentInfoAdapterException, CurationInvalidException { + + AbstractHierarchicalCurationProvider provider = createObjectUnderTest(false); + + ComponentInfoCuration result = provider.findCurations("pkg:/maven/ch.qos.logback/logback-classic@1.2.3", + new SelectorCurationDataHandle(null)); + assertEquals("pkg/maven/ch/qos/logback/logback-classic/1.2.3", result.getName()); + assertEquals(1, result.getCopyrightCurations().size()); + assertTrue(result.getCopyrightCurations().get(0).getNewCopyright().contains("theDefault")); + assertTrue(result.getCopyrightCurations().get(0).getNewCopyright() + .endsWith("pkg/maven/ch/qos/logback/logback-classic/1.2.3")); + } + + @Test + void testFindCurationsInvalidSelector() throws ComponentInfoAdapterNonExistingCurationDataSelectorException, + ComponentInfoAdapterException, CurationInvalidException { + + AbstractHierarchicalCurationProvider provider = createObjectUnderTest(true); + + ComponentInfoAdapterNonExistingCurationDataSelectorException e = assertThrows( + ComponentInfoAdapterNonExistingCurationDataSelectorException.class, + () -> provider.findCurations("pkg:/maven/ch.qos.logback/logback-classic@1.2.3", + new SelectorCurationDataHandle("invalid"))); + assertEquals("test1", e.getMessage()); + } + + @Test + void testFindCurationsNonexistentSelector() throws ComponentInfoAdapterNonExistingCurationDataSelectorException, + ComponentInfoAdapterException, CurationInvalidException { + + AbstractHierarchicalCurationProvider provider = createObjectUnderTest(true); + + ComponentInfoAdapterNonExistingCurationDataSelectorException e = assertThrows( + ComponentInfoAdapterNonExistingCurationDataSelectorException.class, + () -> provider.findCurations("pkg:/maven/ch.qos.logback/logback-classic@1.2.3", + new SelectorCurationDataHandle("nonexistent"))); + assertEquals("test2", e.getMessage()); + } + + /** + * Creates a instance of the {@link AbstractHierarchicalCurationProvider}. Abstract methods will be implemented to + * allow for testing. + * + * @return the object to be tested. + */ + private AbstractHierarchicalCurationProvider createObjectUnderTest(boolean evaluateHierarchy) { + + AllKindsPackageURLHandler packageUrlHandler = Mockito.mock(AllKindsPackageURLHandler.class); + Mockito.when(packageUrlHandler.pathFor("pkg:/maven/ch.qos.logback/logback-classic@1.2.3")) + .thenReturn("pkg/maven/ch/qos/logback/logback-classic/1.2.3"); + + AbstractHierarchicalCurationProvider provider = new AbstractHierarchicalCurationProvider(packageUrlHandler) { + + @Override + protected void validateEffectiveCurationDataSelector(String effectiveCurationDataSelector) + throws ComponentInfoAdapterNonExistingCurationDataSelectorException { + + if (effectiveCurationDataSelector.equals("invalid")) { + throw new ComponentInfoAdapterNonExistingCurationDataSelectorException("test1"); + } + + } + + @Override + protected boolean isHierarchyEvaluation() { + + return evaluateHierarchy; + } + + @Override + protected ComponentInfoCuration fetchCurationFromRepository(String effectiveCurationDataSelector, + String pathFragmentWithinRepo) throws ComponentInfoAdapterException, CurationInvalidException { + + if (effectiveCurationDataSelector.equals("nonexistent") || effectiveCurationDataSelector.equals("empty")) { + return null; + } + if (effectiveCurationDataSelector.equals("curationonlyonversion") + && !pathFragmentWithinRepo.equals("pkg/maven/ch/qos/logback/logback-classic/1.2.3")) { + return null; + } + ComponentInfoCuration cic = new ComponentInfoCuration(); + cic.setName(pathFragmentWithinRepo); + CopyrightCuration cc = new CopyrightCuration(); + cc.setOperation(CurationOperation.ADD); + cc.setNewCopyright("Copyright " + effectiveCurationDataSelector + " " + pathFragmentWithinRepo); + cic.setCopyrightCurations(List.of(cc)); + return cic; + } + + @Override + protected String determineEffectiveCurationDataSelector(String curationDataSelector) { + + if (curationDataSelector == null) { + return "theDefault"; + } else { + return curationDataSelector; + } + } + + @Override + protected void assureCurationDataSelectorAvailable(String effectiveCurationDataSelector) + throws ComponentInfoAdapterException, ComponentInfoAdapterNonExistingCurationDataSelectorException { + + if (effectiveCurationDataSelector.equals("nonexistent")) { + throw new ComponentInfoAdapterNonExistingCurationDataSelectorException("test2"); + } + + } + }; + return provider; + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/CurationReadingTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationReadingTest.java similarity index 58% rename from core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/CurationReadingTest.java rename to core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationReadingTest.java index f7841fd8..ca294d6f 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/CurationReadingTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationReadingTest.java @@ -1,4 +1,4 @@ -package com.devonfw.tools.solicitor.componentinfo.curating; +package com.devonfw.tools.solicitor.componentinfo.curation; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -11,6 +11,7 @@ import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; import com.devonfw.tools.solicitor.componentinfo.curation.model.CurationList; +import com.devonfw.tools.solicitor.componentinfo.curation.model.CurationOperation; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; @@ -23,10 +24,11 @@ class CurationReadingTest { * Test reading a curation for a component which has all fields set. * * @throws IOException might be thown by the ObjectMapper. + * @throws CurationInvalidException * */ @Test - public void testReadSingleCurationComplete() throws IOException { + public void testReadSingleCurationComplete() throws IOException, CurationInvalidException { // given ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); @@ -47,6 +49,34 @@ public void testReadSingleCurationComplete() throws IOException { assertEquals(2, curation.getCopyrights().size()); assertEquals("Copyright (c) 2003 First Holder", curation.getCopyrights().get(0)); + assertEquals(4, curation.getLicenseCurations().size()); + curation.getLicenseCurations().get(0).validate(); + assertEquals("some/path/1", curation.getLicenseCurations().get(0).getPath()); + assertEquals(CurationOperation.REMOVE, curation.getLicenseCurations().get(0).getOperation()); + curation.getLicenseCurations().get(1).validate(); + assertEquals("some/path/2", curation.getLicenseCurations().get(1).getPath()); + assertEquals(CurationOperation.REMOVE, curation.getLicenseCurations().get(1).getOperation()); + curation.getLicenseCurations().get(2).validate(); + assertEquals("another/path/1", curation.getLicenseCurations().get(2).getPath()); + assertEquals(CurationOperation.ADD, curation.getLicenseCurations().get(2).getOperation()); + curation.getLicenseCurations().get(3).validate(); + assertEquals("another/path/2", curation.getLicenseCurations().get(3).getPath()); + assertEquals(CurationOperation.ADD, curation.getLicenseCurations().get(3).getOperation()); + + assertEquals(4, curation.getCopyrightCurations().size()); + curation.getCopyrightCurations().get(0).validate(); + assertEquals("other/path/1", curation.getCopyrightCurations().get(0).getPath()); + assertEquals(CurationOperation.REMOVE, curation.getCopyrightCurations().get(0).getOperation()); + curation.getCopyrightCurations().get(1).validate(); + assertEquals("other/path/2", curation.getCopyrightCurations().get(1).getPath()); + assertEquals(CurationOperation.REMOVE, curation.getCopyrightCurations().get(1).getOperation()); + curation.getCopyrightCurations().get(2).validate(); + assertEquals("some/path/1", curation.getCopyrightCurations().get(2).getPath()); + assertEquals(CurationOperation.ADD, curation.getCopyrightCurations().get(2).getOperation()); + curation.getCopyrightCurations().get(3).validate(); + assertEquals("some/path/2", curation.getCopyrightCurations().get(3).getPath()); + assertEquals(CurationOperation.ADD, curation.getCopyrightCurations().get(3).getOperation()); + } /** @@ -77,7 +107,7 @@ public void testReadSingleCurationPartial() throws IOException { } /** - * Test reading an aarry of curation infos. + * Test reading an array of curation infos. * * @throws IOException might be thown by the ObjectMapper. * @@ -95,11 +125,13 @@ public void testReadListOfCurations() throws IOException { // then assertNotNull(curations); - assertEquals(2, curations.getArtifacts().size()); + assertEquals(4, curations.getArtifacts().size()); assertEquals("pkg/maven/somenamespace/somecomponent/2.3.4", curations.getArtifacts().get(0).getName()); assertEquals("Apache-2.0", curations.getArtifacts().get(0).getLicenses().get(0).getLicense()); assertEquals("pkg/maven/somenamespace/somecomponent/2.3.5", curations.getArtifacts().get(1).getName()); assertEquals("BSD-2-Clause", curations.getArtifacts().get(1).getLicenses().get(0).getLicense()); + assertEquals("pkg/maven/othernamespace", curations.getArtifacts().get(2).getName()); + assertEquals("pkg/maven/othernamespace/othercomponent", curations.getArtifacts().get(3).getName()); } } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java new file mode 100644 index 00000000..efa4b193 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java @@ -0,0 +1,101 @@ +package com.devonfw.tools.solicitor.componentinfo.curation; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.solicitor.common.IOHelper; +import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration; +import com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseInfoCuration; + +/** + * Test for {@link CurationUtil}. + * + */ +class CurationUtilTest { + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.CurationUtil#curationDataFromString(java.lang.String)}. + * + * @throws CurationInvalidException + * @throws IOException + */ + @Test + void testCurationDataFromString() throws CurationInvalidException, IOException { + + try (FileInputStream is = new FileInputStream("src/test/resources/curations/curations.txt")) { + + String testData = IOHelper.readStringFromInputStream(is); + ComponentInfoCuration curations = CurationUtil.curationDataFromString(testData); + assertEquals("some note", curations.getNote()); + } + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.CurationUtil#curationDataFromString(java.lang.String)}. + * + * @throws CurationInvalidException + * @throws IOException + */ + @Test + void testCurationDataFromStringMissingName() throws CurationInvalidException, IOException { + + assertThrows(CurationInvalidException.class, () -> CurationUtil.curationDataFromString("foo")); + } + + /** + * Test method for {@link CurationUtil#merge(ComponentInfoCuration, ComponentInfoCuration)} + */ + @Test + void testMerge() { + + ComponentInfoCuration curationFirst = new ComponentInfoCuration(); + curationFirst.setName("firstName"); + curationFirst.setNote("firstNote"); + curationFirst.setUrl("firstUrl"); + curationFirst.setExcludedPaths(List.of("first path")); + curationFirst.setCopyrights(List.of("first copyright 1", "first copyright 2")); + LicenseInfoCuration licFirst = new LicenseInfoCuration(); + curationFirst.setLicenses(List.of(licFirst)); + CopyrightCuration ccFirst = new CopyrightCuration(); + curationFirst.setCopyrightCurations(List.of(ccFirst)); + LicenseCuration lcFirst = new LicenseCuration(); + curationFirst.setLicenseCurations(List.of(lcFirst)); + + ComponentInfoCuration curationSecond = new ComponentInfoCuration(); + curationSecond.setName("secondName"); + curationSecond.setNote("secondNote"); + curationSecond.setUrl("secondUrl"); + curationSecond.setExcludedPaths(List.of("second path")); + curationSecond.setCopyrights(List.of("second copyright 1", "second copyright 2")); + LicenseInfoCuration licSecond = new LicenseInfoCuration(); + curationSecond.setLicenses(List.of(licSecond)); + CopyrightCuration ccSecond = new CopyrightCuration(); + curationSecond.setCopyrightCurations(List.of(ccSecond)); + LicenseCuration lcSecond = new LicenseCuration(); + curationSecond.setLicenseCurations(List.of(lcSecond)); + + ComponentInfoCuration merged = CurationUtil.merge(curationFirst, curationSecond); + assertEquals("secondName", merged.getName()); + assertTrue(merged.getNote().startsWith("secondNote")); + assertTrue(merged.getNote().contains("/")); + assertTrue(merged.getNote().endsWith("firstNote")); + assertEquals("secondUrl", merged.getUrl()); + assertTrue(merged.getCopyrights() + .equals(List.of("second copyright 1", "second copyright 2", "first copyright 1", "first copyright 2"))); + assertTrue(merged.getLicenses().equals(List.of(licSecond, licFirst))); + assertTrue(merged.getExcludedPaths().equals(List.of("second path", "first path"))); + assertTrue(merged.getLicenseCurations().equals(List.of(lcSecond, lcFirst))); + assertTrue(merged.getCopyrightCurations().equals(List.of(ccSecond, ccFirst))); + } +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProviderTest.java similarity index 71% rename from core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java rename to core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProviderTest.java index e95082ac..00970f81 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curating/SingleFileCurationProviderTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProviderTest.java @@ -1,4 +1,4 @@ -package com.devonfw.tools.solicitor.componentinfo.curating; +package com.devonfw.tools.solicitor.componentinfo.curation; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -10,7 +10,6 @@ import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; -import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; import com.devonfw.tools.solicitor.componentinfo.curation.model.ComponentInfoCuration; /** @@ -29,6 +28,8 @@ public void setup() { .thenReturn("pkg/maven/somenamespace/somecomponent/2.3.4"); Mockito.when(packageUrlHandler.pathFor("pkg:maven/somenamespace/somecomponent@2.3.5")) .thenReturn("pkg/maven/somenamespace/somecomponent/2.3.5"); + Mockito.when(packageUrlHandler.pathFor("pkg:maven/othernamespace/othercomponent@1.2.3")) + .thenReturn("pkg/maven/othernamespace/othercomponent/1.2.3"); this.objectUnderTest = new SingleFileCurationProvider(packageUrlHandler); this.objectUnderTest.setCurationsFileName("src/test/resources/curations/array_of_curations.yaml"); @@ -39,9 +40,10 @@ public void setup() { * {@link com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider#findCurations(java.lang.String, java.lang.String)}. * * @throws ComponentInfoAdapterException + * @throws CurationInvalidException */ @Test - void testFindCurationsWithSelectorNull() throws ComponentInfoAdapterException { + void testFindCurationsWithSelectorNull() throws ComponentInfoAdapterException, CurationInvalidException { ComponentInfoCuration result; @@ -59,9 +61,10 @@ void testFindCurationsWithSelectorNull() throws ComponentInfoAdapterException { * {@link com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider#findCurations(java.lang.String, java.lang.String)}. * * @throws ComponentInfoAdapterException + * @throws CurationInvalidException */ @Test - void testFindCurationsWithSelectorNone() throws ComponentInfoAdapterException { + void testFindCurationsWithSelectorNone() throws ComponentInfoAdapterException, CurationInvalidException { ComponentInfoCuration result; @@ -69,4 +72,23 @@ void testFindCurationsWithSelectorNone() throws ComponentInfoAdapterException { new SelectorCurationDataHandle("none")); assertNull(result); } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider#findCurations(java.lang.String, java.lang.String)}. + * + * @throws ComponentInfoAdapterException + * @throws CurationInvalidException + */ + @Test + void testFindCurationsUsingHierarchy() throws ComponentInfoAdapterException, CurationInvalidException { + + ComponentInfoCuration result; + + result = this.objectUnderTest.findCurations("pkg:maven/othernamespace/othercomponent@1.2.3", + new SelectorCurationDataHandle(null)); + assertEquals("some/path/2", result.getLicenseCurations().get(0).getPath()); + assertEquals("some/path/1", result.getLicenseCurations().get(1).getPath()); + } + } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CopyrightCurationTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CopyrightCurationTest.java new file mode 100644 index 00000000..6151a1fc --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/model/CopyrightCurationTest.java @@ -0,0 +1,162 @@ +package com.devonfw.tools.solicitor.componentinfo.curation.model; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; + +/** + * Tests for {@link CopyrightCuration}. Note that not all possible data constellations are tested but just the main + * behavior. + * + */ +class CopyrightCurationTest { + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration#matches(java.lang.String, java.lang.String)}. + */ + @Test + void testMatchesStringString() { + + CopyrightCuration cc = new CopyrightCuration(); + cc.setOperation(CurationOperation.ADD); + + assertFalse(cc.matches(null, null)); + + cc.setOperation(CurationOperation.REMOVE); + assertTrue(cc.matches(null, null)); + + cc.setPath(".*abc.*"); + cc.setOldCopyright(".*456.*"); + assertTrue(cc.matches("abcd", "4567")); + assertFalse(cc.matches("bcd", "4567")); + assertFalse(cc.matches("abcd", "567")); + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration#matches(java.lang.String)}. + */ + @Test + void testMatchesString() { + + CopyrightCuration cc = new CopyrightCuration(); + cc.setOperation(CurationOperation.REMOVE); + + assertFalse(cc.matches(null)); + + cc.setOperation(CurationOperation.ADD); + assertTrue(cc.matches(null)); + assertFalse(cc.matches("foo")); + + cc.setPath(".*abc.*"); + assertTrue(cc.matches("abcd")); + assertFalse(cc.matches("bcd")); + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration#validate()}. + * + * @throws CurationInvalidException if the curation is not valid + */ + @Test + void testValidateNoOp() throws CurationInvalidException { + + // no operation defined + CopyrightCuration cc = new CopyrightCuration(); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + cc.validate(); + }, "Operation must not be null for copyright curation"); + + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration#validate()}. + * + * @throws CurationInvalidException if the curation is not valid + */ + @Test + void testValidateAdd() throws CurationInvalidException { + + CopyrightCuration cc = new CopyrightCuration(); + + cc.setOperation(CurationOperation.ADD); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + cc.validate(); + }, "For ADD copyright curation newCopyright must be set"); + + cc.setNewCopyright("foo"); + + cc.validate(); // should be ok + + cc.setOldCopyright("bar"); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + cc.validate(); + }, "For ADD copyright curation oldCopyright must not be defined"); + + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration#validate()}. + * + * @throws CurationInvalidException if the curation is not valid + */ + @Test + void testValidateRemove() throws CurationInvalidException { + + CopyrightCuration cc = new CopyrightCuration(); + + cc.setOperation(CurationOperation.REMOVE); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + cc.validate(); + }, "For REMOVE/REPLACE copyright curation at least one condition must be defined"); + + cc.setPath("foo"); + + cc.validate(); // should be ok + + cc.setNewCopyright("bar"); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + cc.validate(); + }, "For REMOVE copyright curation the newCopyright must not be set"); + + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.CopyrightCuration#validate()}. + * + * @throws CurationInvalidException if the curation is not valid + */ + @Test + void testValidateReplace() throws CurationInvalidException { + + CopyrightCuration cc = new CopyrightCuration(); + + cc.setOperation(CurationOperation.REPLACE); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + cc.validate(); + }, "For REMOVE/REPLACE copyright curation at least one condition must be defined"); + + cc.setPath("foo"); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + cc.validate(); + }, "For REPLACE copyright curation newCopyright must be set"); + + cc.setNewCopyright("bar"); + + cc.validate(); // should be ok + + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseCurationTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseCurationTest.java new file mode 100644 index 00000000..0e2c6973 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/model/LicenseCurationTest.java @@ -0,0 +1,198 @@ +package com.devonfw.tools.solicitor.componentinfo.curation.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; +import com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration.NewLicenseData; + +/** + * Tests for {@link LicenseCuration}. Note that not all possible data constellations are tested but just the main + * behavior. + * + */ +class LicenseCurationTest { + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration#matches(java.lang.String, java.lang.String, java.lang.String, java.lang.String)}. + */ + @Test + void testMatchesStringStringStringString() { + + LicenseCuration lc = new LicenseCuration(); + lc.setOperation(CurationOperation.ADD); + + assertFalse(lc.matches(null, null, null, null)); + + lc.setOperation(CurationOperation.REMOVE); + assertTrue(lc.matches(null, null, null, null)); + + lc.setPath(".*abc.*"); + lc.setRuleIdentifier(".*def.*"); + lc.setMatchedText(".*123.*"); + lc.setOldLicense(".*456.*"); + assertTrue(lc.matches("abcd", "cdef", "z123", "4567")); + assertFalse(lc.matches("bcd", "cdef", "z123", "4567")); + assertFalse(lc.matches("abcd", "cde", "z123", "4567")); + assertFalse(lc.matches("abcd", "cdef", "z12", "4567")); + assertFalse(lc.matches("abcd", "cdef", "z123", "567")); + + } + + /** + * Test method for + * {@link com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration#matches(java.lang.String)}. + */ + @Test + void testMatchesString() { + + LicenseCuration lc = new LicenseCuration(); + lc.setOperation(CurationOperation.REMOVE); + + assertFalse(lc.matches(null)); + + lc.setOperation(CurationOperation.ADD); + assertTrue(lc.matches(null)); + assertFalse(lc.matches("foo")); + + lc.setPath(".*abc.*"); + assertTrue(lc.matches("abcd")); + assertFalse(lc.matches("bcd")); + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration#newLicenseData()}. + */ + @Test + void testNewLicenseData() { + + LicenseCuration lc = new LicenseCuration(); + lc.setOperation(CurationOperation.REMOVE); + + assertNull(lc.newLicenseData()); + + lc.setOperation(CurationOperation.ADD); + NewLicenseData nld = lc.newLicenseData(); + + assertNull(nld.license); + assertNull(nld.url); + + lc.setNewLicense("foo"); + lc.setUrl("bar"); + + nld = lc.newLicenseData(); + + assertEquals("foo", nld.license); + assertEquals("bar", nld.url); + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration#validate()}. + * + * @throws CurationInvalidException if the curation is not valid + */ + @Test + void testValidateNoOp() throws CurationInvalidException { + + // no operation defined + LicenseCuration lc = new LicenseCuration(); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + lc.validate(); + }, "Operation must not be null for license curation"); + + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration#validate()}. + * + * @throws CurationInvalidException if the curation is not valid + */ + @Test + void testValidateAdd() throws CurationInvalidException { + + LicenseCuration lc = new LicenseCuration(); + + lc.setOperation(CurationOperation.ADD); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + lc.validate(); + }, "For ADD license curation license and url must be set"); + + lc.setNewLicense("foo"); + Assertions.assertThrows(CurationInvalidException.class, () -> { + lc.validate(); + }, "For ADD license curation license and url must be set"); + + lc.setUrl("bar"); + lc.validate(); // should be ok + + lc.setMatchedText("some text"); + Assertions.assertThrows(CurationInvalidException.class, () -> { + lc.validate(); + }, "For ADD license curation at neither ruleIdentifier nor matchedText nor oldLicense must be defined"); + + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration#validate()}. + * + * @throws CurationInvalidException if the curation is not valid + */ + @Test + void testValidateRemove() throws CurationInvalidException { + + LicenseCuration lc = new LicenseCuration(); + + lc.setOperation(CurationOperation.REMOVE); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + lc.validate(); + }, "For REMOVE/REPLACE license curation at least one condition must be defined"); + + lc.setMatchedText("some text"); + + lc.validate(); // should be ok + + lc.setNewLicense("foo"); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + lc.validate(); + }, "For REMOVE license curation neither newLicense nor url must be set"); + + } + + /** + * Test method for {@link com.devonfw.tools.solicitor.componentinfo.curation.model.LicenseCuration#validate()}. + * + * @throws CurationInvalidException if the curation is not valid + */ + @Test + void testValidateReplace() throws CurationInvalidException { + + LicenseCuration lc = new LicenseCuration(); + + lc.setOperation(CurationOperation.REPLACE); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + lc.validate(); + }, "For REMOVE/REPLACE license curation at least one condition must be defined"); + + lc.setMatchedText("some text"); + + Assertions.assertThrows(CurationInvalidException.class, () -> { + lc.validate(); + }, "For REPLACE license curation at least license or url must be set"); + + lc.setNewLicense("foo"); + + lc.validate(); // should be ok + + } +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java index 0d5cafdb..5e95bb00 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FilteredScancodeComponentInfoProviderTests.java @@ -11,6 +11,7 @@ import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; /** @@ -49,9 +50,10 @@ public void setup() { * file exists * * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException if the curation data is not valid */ @Test - public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterException { + public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterException, CurationInvalidException { // given this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/nonexisting.yaml"); @@ -76,9 +78,11 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx * excluded * * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException if the curation data is not valid */ @Test - public void testGetComponentInfoWithCurationsAndExclusions() throws ComponentInfoAdapterException { + public void testGetComponentInfoWithCurationsAndExclusions() + throws ComponentInfoAdapterException, CurationInvalidException { // given this.singleFileCurationProvider @@ -104,9 +108,11 @@ public void testGetComponentInfoWithCurationsAndExclusions() throws ComponentInf * paths are excluded * * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException if the curation data is not valid */ @Test - public void testGetComponentInfoWithCurationsAndWithoutExclusions() throws ComponentInfoAdapterException { + public void testGetComponentInfoWithCurationsAndWithoutExclusions() + throws ComponentInfoAdapterException, CurationInvalidException { // given this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/curations.yaml"); diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/RawCurationTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/RawCurationTest.java new file mode 100644 index 00000000..d749c27d --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/RawCurationTest.java @@ -0,0 +1,663 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfo; +import com.devonfw.tools.solicitor.componentinfo.ComponentInfoAdapterException; +import com.devonfw.tools.solicitor.componentinfo.DataStatusValue; +import com.devonfw.tools.solicitor.componentinfo.LicenseInfo; +import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; +import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; + +/** + * This class contains JUnit test methods for the testing the raw curations of + * {@link FilteredScancodeComponentInfoProvider} class. + */ +public class RawCurationTest { + + // the object under test + FilteredScancodeComponentInfoProvider filteredScancodeComponentInfoProvider; + + FileScancodeRawComponentInfoProvider fileScancodeRawComponentInfoProvider; + + SingleFileCurationProvider singleFileCurationProvider; + + @BeforeEach + public void setup() { + + AllKindsPackageURLHandler packageURLHandler = Mockito.mock(AllKindsPackageURLHandler.class); + + Mockito.when(packageURLHandler.pathFor("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0")) + .thenReturn("pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0"); + + this.fileScancodeRawComponentInfoProvider = new FileScancodeRawComponentInfoProvider(packageURLHandler); + this.fileScancodeRawComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); + + this.singleFileCurationProvider = new SingleFileCurationProvider(packageURLHandler); + this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/curations.yaml"); + + this.filteredScancodeComponentInfoProvider = new FilteredScancodeComponentInfoProvider( + this.fileScancodeRawComponentInfoProvider, packageURLHandler, this.singleFileCurationProvider); + + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseRemove_AllConditionsSetAndMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_1.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(1, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + assertEquals("Apache-2.0", + scancodeComponentInfo.getComponentInfoData().getLicenses().iterator().next().getSpdxid()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseRemove_AllConditionsSetAndPathNotMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_2.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertNotEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseRemove_AllConditionsSetAndRuleIdentifierNotMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_3.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertNotEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseRemove_AllConditionsSetAndOldLicenseNotMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_4.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertNotEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseRemove_AllConditionsSetAndMatchedTextNotMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_5.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertNotEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseRemove_OnlyPathConditionSetAndMetForAllFiles() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_7.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(0, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseReplace_AllConditionsSetAndMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_1.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + boolean found = false; + for (LicenseInfo license : scancodeComponentInfo.getComponentInfoData().getLicenses()) { + if (license.getSpdxid().equals("NewLicense")) { + found = true; + assertEquals("pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3", license.getLicenseUrl()); + assertTrue(license.getGivenLicenseText() + .startsWith(" * This file is part of the test data for deep license scan support in Solicitor..")); + } + } + assertTrue(found); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseReplace_AllConditionsSetAndMetOnlyLicenseReplaced() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_2.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + boolean found = false; + for (LicenseInfo license : scancodeComponentInfo.getComponentInfoData().getLicenses()) { + if (license.getSpdxid().equals("NewLicense")) { + found = true; + assertEquals("pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L4", license.getLicenseUrl()); + assertTrue(license.getGivenLicenseText() + .startsWith(" * It is licensed under the same license as the rest of Solicitor.")); + } + } + assertTrue(found); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseReplace_AllConditionsSetAndMetOnlyUrlReplaced() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_3.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + boolean found = false; + for (LicenseInfo license : scancodeComponentInfo.getComponentInfoData().getLicenses()) { + if (license.getSpdxid().equals("LicenseRef-scancode-unknown-license-reference")) { + found = true; + assertEquals("pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3", license.getLicenseUrl()); + assertTrue(license.getGivenLicenseText() + .startsWith(" * This file is part of the test data for deep license scan support in Solicitor..")); + } + } + assertTrue(found); + + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseAdd_WithPath() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_add_curation_1.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(3, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + boolean found = false; + for (LicenseInfo license : scancodeComponentInfo.getComponentInfoData().getLicenses()) { + if (license.getSpdxid().equals("NewLicense")) { + found = true; + assertEquals("pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3", license.getLicenseUrl()); + assertTrue(license.getGivenLicenseText() + .startsWith(" * This file is part of the test data for deep license scan support in Solicitor..")); + } + } + assertTrue(found); + + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseAdd_WithoutPath() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_add_curation_2.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(3, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + boolean found = false; + for (LicenseInfo license : scancodeComponentInfo.getComponentInfoData().getLicenses()) { + if (license.getSpdxid().equals("NewLicense")) { + found = true; + assertEquals("pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3", license.getLicenseUrl()); + assertTrue(license.getGivenLicenseText() + .startsWith(" * This file is part of the test data for deep license scan support in Solicitor..")); + } + } + assertTrue(found); + + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseAdd_WithPathNotMatching() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/license_add_curation_3.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertNotEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getLicenses().size()); + + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawCopyrightRemove_AllConditionsSetAndMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_1.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(0, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawCopyrightRemove_AllConditionsSetAndPathNotMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_2.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertNotEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(1, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + assertEquals("Copyright 2023 devonfw", + scancodeComponentInfo.getComponentInfoData().getCopyrights().iterator().next()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawLicenseRemove_AllConditionsSetAndOldCopyrightNotMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_4.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertNotEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(1, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + assertEquals("Copyright 2023 devonfw", + scancodeComponentInfo.getComponentInfoData().getCopyrights().iterator().next()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawCopyrightRemove_OnlyPathConditionSetAndMetForAllFiles() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_7.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(0, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawCopyrightReplace_AllConditionsSetAndMet() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/copyright_replace_curation_1.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(1, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + assertEquals("(c) 2023 devonfw", scancodeComponentInfo.getComponentInfoData().getCopyrights().iterator().next()); + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawCopyrightAdd_WithPath() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_1.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + assertTrue(scancodeComponentInfo.getComponentInfoData().getCopyrights().contains("(c) 2024 devonfw")); + assertTrue(scancodeComponentInfo.getComponentInfoData().getCopyrights().contains("Copyright 2023 devonfw")); + + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawCopyrightAdd_WithoutPath() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_2.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(2, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + assertTrue(scancodeComponentInfo.getComponentInfoData().getCopyrights().contains("(c) 2024 devonfw")); + assertTrue(scancodeComponentInfo.getComponentInfoData().getCopyrights().contains("Copyright 2023 devonfw")); + + } + + /** + * Test the + * {@link FilteredScancodeComponentInfoProvider#getComponentInfo(String, com.devonfw.tools.solicitor.componentinfo.CurationDataHandle)} + * + * + * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException + */ + @Test + public void testGetComponentInfoRawCopyrightAdd_WithPathNotMatching() + throws ComponentInfoAdapterException, CurationInvalidException { + + // given + this.singleFileCurationProvider + .setCurationsFileName("src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_3.yaml"); + + // when + ComponentInfo scancodeComponentInfo = this.filteredScancodeComponentInfoProvider.getComponentInfo( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", + new SelectorCurationDataHandle("someCurationSelector")); + + // then + assertNotNull(scancodeComponentInfo.getComponentInfoData()); + assertNotEquals(DataStatusValue.CURATED, scancodeComponentInfo.getDataStatus()); + assertEquals(1, scancodeComponentInfo.getComponentInfoData().getCopyrights().size()); + assertTrue(scancodeComponentInfo.getComponentInfoData().getCopyrights().contains("Copyright 2023 devonfw")); + + } + +} diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java index ad713cba..acb981e5 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/ScancodeComponentInfoAdapterTest.java @@ -20,6 +20,7 @@ import com.devonfw.tools.solicitor.componentinfo.SelectorCurationDataHandle; import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCurator; import com.devonfw.tools.solicitor.componentinfo.curation.ComponentInfoCuratorImpl; +import com.devonfw.tools.solicitor.componentinfo.curation.CurationInvalidException; import com.devonfw.tools.solicitor.componentinfo.curation.CurationProvider; import com.devonfw.tools.solicitor.componentinfo.curation.SingleFileCurationProvider; @@ -75,9 +76,10 @@ public void setup() { * known. * * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException */ @Test - public void testGetComponentInfoNaPackage() throws ComponentInfoAdapterException { + public void testGetComponentInfoNaPackage() throws ComponentInfoAdapterException, CurationInvalidException { // given @@ -94,9 +96,10 @@ public void testGetComponentInfoNaPackage() throws ComponentInfoAdapterException * available. * * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException */ @Test - public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterException { + public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterException, CurationInvalidException { // given this.singleFileCurationProvider.setCurationsFileName("src/test/resources/scancodefileadapter/nonexisting.yaml"); @@ -144,9 +147,11 @@ public void testGetComponentInfoWithoutCurations() throws ComponentInfoAdapterEx * curationDataSelector to downstream beans. * * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException */ @Test - public void testGetComponentCheckCurationDataSelector() throws ComponentInfoAdapterException { + public void testGetComponentCheckCurationDataSelector() + throws ComponentInfoAdapterException, CurationInvalidException { // given CurationProvider curationProvider = Mockito.mock(CurationProvider.class); @@ -178,9 +183,10 @@ public void testGetComponentCheckCurationDataSelector() throws ComponentInfoAdap * Test the {@link ScancodeComponentInfoAdapter#getComponentInfo(String,String)} method when curations are existing. * * @throws ComponentInfoAdapterException if something goes wrong + * @throws CurationInvalidException */ @Test - public void testGetComponentInfoWithCurations() throws ComponentInfoAdapterException { + public void testGetComponentInfoWithCurations() throws ComponentInfoAdapterException, CurationInvalidException { // when ComponentInfo componentInfo = this.scancodeComponentInfoAdapter.getComponentInfo( diff --git a/core/src/test/resources/curations/array_of_curations.yaml b/core/src/test/resources/curations/array_of_curations.yaml index 5ff9e40a..8adae30c 100644 --- a/core/src/test/resources/curations/array_of_curations.yaml +++ b/core/src/test/resources/curations/array_of_curations.yaml @@ -11,3 +11,17 @@ artifacts: - license: "BSD-2-Clause" url: "https://scancode-licensedb.aboutcode.org/bsd-simplified.LICENSE" copyrights: [] +- name: "pkg/maven/othernamespace" + licenseCurations: + - path: some/path/1 + operation: REMOVE + ruleIdentifier: rule_1 + matchedText: some text 1 + comment: just for testing +- name: "pkg/maven/othernamespace/othercomponent" + licenseCurations: + - path: "some/path/2" + operation: REMOVE + ruleIdentifier: "rule_2" + matchedText: "some text 2" + comment: "just for second test" diff --git a/core/src/test/resources/curations/curation_with_all_fields_set.yaml b/core/src/test/resources/curations/curation_with_all_fields_set.yaml index f1a6cd6d..087f6e15 100644 --- a/core/src/test/resources/curations/curation_with_all_fields_set.yaml +++ b/core/src/test/resources/curations/curation_with_all_fields_set.yaml @@ -11,3 +11,41 @@ licenses: copyrights: - "Copyright (c) 2003 First Holder" - "Copyright (c) 2004 Second Holder" +licenseCurations: +- path: some/path/1 + operation: REMOVE + ruleIdentifier: rule_1 + matchedText: some text 1 + comment: just for testing +- path: "some/path/2" + operation: REMOVE + ruleIdentifier: "rule_2" + matchedText: "some text 2" + comment: "just for second test" +- path: another/path/1 + operation: ADD + newLicense: Apache-2.0 + url: https://scancode-licensedb.aboutcode.org/apache-2.0.LICENSE + comment: just for testing 3 +- path: another/path/2 + operation: ADD + newLicense: MIT + url: https://scancode-licensedb.aboutcode.org/MIT.LICENSE + comment: just for testing 4 +copyrightCurations: +- path: other/path/1 + operation: REMOVE + oldCopyright: "(c) 2024 me" + comment: testing 1 +- path: other/path/2 + operation: REMOVE + oldCopyright: "(c) 2024 you" + comment: testing 2 +- path: some/path/1 + operation: ADD + newCopyright: "(c) 2024 he" + comment: testing 3 +- path: some/path/2 + operation: ADD + newCopyright: "(c) 2024 them" + comment: testing 2 diff --git a/core/src/test/resources/curations/curations.txt b/core/src/test/resources/curations/curations.txt new file mode 100644 index 00000000..8e7bb991 --- /dev/null +++ b/core/src/test/resources/curations/curations.txt @@ -0,0 +1,12 @@ +name: "pkg/maven/ch/qos/logback/logback-classic/1.2.3" +note: "some note" +copyrights: +- "Copyright (c) 1999-2010, QOS.ch" +- "Copyright (c) 1999-2012, QOS.ch" +- "Copyright (c) 1999-2015, QOS.ch" +- "Copyright (c) 1999-2016, QOS.ch" +licenses: +- license: "EPL-1.0" + url: "pkgcontent:/ch/qos/logback/classic/AsyncAppender.java#L5-L12" +- license: "LGPL-2.1-only" + url: "pkgcontent:/ch/qos/logback/classic/AsyncAppender.java#L5-L12" \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_1.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_1.yaml new file mode 100644 index 00000000..9d34f7ef --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_1.yaml @@ -0,0 +1,8 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrightCurations: + - operation: ADD + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + newCopyright: "(c) 2024 devonfw" + comment: "add rule with path" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_2.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_2.yaml new file mode 100644 index 00000000..31b22375 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_2.yaml @@ -0,0 +1,7 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrightCurations: + - operation: ADD + newCopyright: "(c) 2024 devonfw" + comment: "add rule with no condition" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_3.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_3.yaml new file mode 100644 index 00000000..6451af7d --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_add_curation_3.yaml @@ -0,0 +1,8 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrightCurations: + - operation: ADD + path: "sources/src/main/java/com/devonfw/WRONG/tools/test/SampleClass2.java" + newCopyright: "(c) 2024 devonfw" + comment: "add rule with path which does not match" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_1.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_1.yaml new file mode 100644 index 00000000..445832e7 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_1.yaml @@ -0,0 +1,8 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrightCurations: + - operation: REMOVE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass1.java" + oldCopyright: "Copyright 2023 devonfw" + comment: "remove rule with all conditions set" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_2.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_2.yaml new file mode 100644 index 00000000..275d8b3b --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_2.yaml @@ -0,0 +1,8 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrightCurations: + - operation: REMOVE + path: "sources/src/main/java/com/devonfw/WRONG/tools/test/SampleClass1.java" + oldCopyright: "Copyright 2023 devonfw" + comment: "remove rule with all conditions set" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_4.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_4.yaml new file mode 100644 index 00000000..0a021d1d --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_4.yaml @@ -0,0 +1,8 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrightCurations: + - operation: REMOVE + path: "sources/src/main/java/com/devonfw/WRONG/tools/test/SampleClass1.java" + oldCopyright: "Copyright WRONG 2023 devonfw" + comment: "remove rule with all conditions set" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_7.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_7.yaml new file mode 100644 index 00000000..e774b3ff --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_remove_curation_7.yaml @@ -0,0 +1,8 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrightCurations: + - operation: REMOVE + path: "sources/.*" + # oldCopyright: "Copyright 2023 devonfw" + comment: "remove rule matching to all files/copyrights" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/copyright_replace_curation_1.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_replace_curation_1.yaml new file mode 100644 index 00000000..c8c8a3d6 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/copyright_replace_curation_1.yaml @@ -0,0 +1,9 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + copyrightCurations: + - operation: REPLACE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass1.java" + oldCopyright: "Copyright 2023 devonfw" + newCopyright: "(c) 2023 devonfw" + comment: "replace rule with all conditions set" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_1.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_1.yaml new file mode 100644 index 00000000..23467b44 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_1.yaml @@ -0,0 +1,8 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: ADD + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + newLicense: "NewLicense" + url: "pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_2.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_2.yaml new file mode 100644 index 00000000..a8f27c5b --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_2.yaml @@ -0,0 +1,7 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: ADD + newLicense: "NewLicense" + url: "pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_3.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_3.yaml new file mode 100644 index 00000000..c81f3342 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_add_curation_3.yaml @@ -0,0 +1,8 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: ADD + path: "sources/src/main/java/com/devonfw/WRONG/tools/test/SampleClass2.java" + newLicense: "NewLicense" + url: "pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_1.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_1.yaml new file mode 100644 index 00000000..086da595 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_1.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REMOVE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + ruleIdentifier: "license-intro_26.RULE" + oldLicense: "LicenseRef-scancode-unknown-license-reference" + matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "remove rule with all conditions set" \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_2.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_2.yaml new file mode 100644 index 00000000..d837fb6a --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_2.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REMOVE + path: "sources/src/main/java/WRONG/com/devonfw/tools/test/SampleClass2.java" + ruleIdentifier: "license-intro_26.RULE" + oldLicense: "LicenseRef-scancode-unknown-license-reference" + matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "remove rule with all conditions set" \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_3.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_3.yaml new file mode 100644 index 00000000..f8608fd7 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_3.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REMOVE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + ruleIdentifier: "license-WRONG-intro_26.RULE" + oldLicense: "LicenseRef-scancode-unknown-license-reference" + matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "remove rule with all conditions set" \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_4.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_4.yaml new file mode 100644 index 00000000..ca2d1367 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_4.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REMOVE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + ruleIdentifier: "license-intro_26.RULE" + oldLicense: "LicenseRef-scancode-WRONG-unknown-license-reference" + matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "remove rule with all conditions set" \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_5.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_5.yaml new file mode 100644 index 00000000..a08d5759 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_5.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REMOVE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + ruleIdentifier: "license-intro_26.RULE" + oldLicense: "LicenseRef-scancode-unknown-license-reference" + matchedText: ".*It is licensed under the WRONG same license as the rest of Solicitor.*" + comment: "remove rule with all conditions set" \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_6.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_6.yaml new file mode 100644 index 00000000..436f964a --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_6.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REMOVE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + #ruleIdentifier: "license-intro_26.RULE" + #oldLicense: "LicenseRef-scancode-unknown-license-reference" + #matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "remove rule with only path condition set" \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_7.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_7.yaml new file mode 100644 index 00000000..0b6b75bf --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_remove_curation_7.yaml @@ -0,0 +1,10 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REMOVE + path: "sources/.*" + #ruleIdentifier: "license-intro_26.RULE" + #oldLicense: "LicenseRef-scancode-unknown-license-reference" + #matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "remove rule with only path condition set which matches for all files" \ No newline at end of file diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_1.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_1.yaml new file mode 100644 index 00000000..36ec5f37 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_1.yaml @@ -0,0 +1,12 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REPLACE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + ruleIdentifier: "license-intro_26.RULE" + oldLicense: "LicenseRef-scancode-unknown-license-reference" + matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "replace rule with all conditions set" + newLicense: "NewLicense" + url: "pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_2.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_2.yaml new file mode 100644 index 00000000..df5c4ec1 --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_2.yaml @@ -0,0 +1,12 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REPLACE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + ruleIdentifier: "license-intro_26.RULE" + oldLicense: "LicenseRef-scancode-unknown-license-reference" + matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "replace rule with all conditions set" + newLicense: "NewLicense" + # url: "new url" diff --git a/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_3.yaml b/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_3.yaml new file mode 100644 index 00000000..3c0cd10f --- /dev/null +++ b/core/src/test/resources/scancodefileadapter/rawcurations/license_replace_curation_3.yaml @@ -0,0 +1,12 @@ +artifacts: +- name: "pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0" + note: "some note on the curation" + licenseCurations: + - operation: REPLACE + path: "sources/src/main/java/com/devonfw/tools/test/SampleClass2.java" + ruleIdentifier: "license-intro_26.RULE" + oldLicense: "LicenseRef-scancode-unknown-license-reference" + matchedText: ".*It is licensed under the same license as the rest of Solicitor.*" + comment: "replace rule with all conditions set" + #newLicense: "NewLicense" + url: "pkgcontent:/src/main/java/com/devonfw/tools/test/SampleClass2.java#L3" diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 78aaae4a..5582dff3 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1561,11 +1561,125 @@ solicitor.feature-flag.scancode.automapping=false === Correcting data The data obtained from ScanCode might be affected by false positives (wrongly detected a license or copyright) or false negatives (missed to detect a license or copyright). To compensate such defects there are two mechanisms: -Applying Curation information from a "curations" file or changing the License information via the decision table rules. +Applying Curation information from a "curations" file or changing the license information via the decision table rules. -==== Curations file +==== Curating data via a curations file + +===== Structure of curations file To define curations you might create a file `output/curations.yaml` containing the following structure: +[source,yaml] +---- +artifacts: + - name: pkg/npm/@somescope/somepackage/1.2.3 <1> + url: https://github.com/foo/bar <2> + licenseCurations: <3> + - operation: REMOVE + path: "sources/package/readme.md" + ruleIdentifier: "proprietary-license_unknown_13.RULE" + matchedText: ".* to be paid .*" + comment: "just a generic remark, not a license" + - operation: ADD + newLicense: "Apache-2.0" + comment: "License as given on website" + copyrightCurations: <4> + - operation: REMOVE + path: "sources/package/lib/test.js" + oldCopyright: "(c) R.apv" + comment: "some minified code fragment, not a copyright" + excludedPaths: <5> + - "sources/src" <6> + - name: pkg/npm/@anotherscope/anotherpackage/4.5.6 <7> +. +. +. +---- +<1> Path of the package information as used in the file tree. Derived from the PackageURL. +<2> URL of the project, will be stored as `sourceRepoUrl`. (Optional: no change if not existing.) +<3> Rules for curating license findings, see below. +<4> Rules for curation copyright findings, see below. +<5> Excluded paths to be set. Optional. If defined then all scanned files, whose path prefix contain any given string here, are excluded from the ScanCode information. +<6> A single path prefix. All scanned files starting with this path prefix are excluded from the Scancode information. +<7> Further packages to follow. + +===== Rules for curating licenses +Curating licenses is done by REMOVING (i.e. ignoring) specific license findings from ScanCode, by REPLACING the detected license with another one or by ADDING license findings either to specific files or on top level (not related to specific file of the package sources). +In addition to the conditions/data which is specific for any of the below described operations it is always possible to define a comment which is intended to be included in any audit trail log for documentation purposes (not yet used/implemented). + + +====== Licenses: REMOVE +Removing found licenses is done by defining rules which result in ignoring the license finding(s) of scancode rules in files within the scanned codebase. The following "conditions" are used for defining the rule + +* `path` of the file within the sources (defined as a regular expression; matches to `files[].path` in the scancode json file) +* `ruleIdentifier` of the rule (defined as a regular expression; matches to `files[].licenses[].matched_rule.identifier` in the scancode json file) +* `matchedText` of the finding (defined as a regular expression; matches to `files[].licenses[].matched_text` in the scancode json file) +* `oldLicense` of the finding (defined as regular expression; matches to `files[].licenses[].spdx_license_key` + +The first three conditions can uniquely identify any license finding listed in the scancode json file. The `oldLicense` condition can be used to select findings to be ignored based on the found license instead of the `ruleIdentifier`. +All conditions are optional but at least one needs to be defined. By using RegEx syntax the curations can be written very flexible. By using solely `oldLicense` as a condition it is e.g. possible to remove all findings of a specific license. + +====== Licenses: REPLACE +Instead of removing licenses (ignoring the finding) they might be replaced with a different license key and/or URL pointing to the license text. The conditions are the same as for REMOVE, the replacement is defined as follows + +Data: + +* `newLicense` is the key / id of the license to use instead (replacing `files[].licenses[].spdx_license_key`) +* `url` is the url pointing to the license text + +At least one of the two parameters has to be set. + +====== Licenses: ADD +Adding new licenses is done by defining rules which add new license info (to the licenses found in a source file) - or "on top level". + +Conditions: + +* `path` of the file within the sources to which the license should be added (defined as a regular expression; matches to `files[].path` in the scancode json file). Note that this will only work if there are `files[].path` in the scancode json for which this conditions matches. + +It is not possible to associate licenses to files which are not listed in scancode json. The `path` condition might be omitted which results in the given license to be added to the result without any relation to a specific path. + +Data: + +* `newLicense`: the key/SPDX-ID of the license to add +* `url`: URL to the license text + +===== Rules for curating copyrights +Curating copyrights is based on the same principles as curation of licenses, providing REMOVE, REPLACE and ADD operations. + + +====== Copyrights: REMOVE +REMOVING found copyrights is done by defining rules which result in ignoring the copyright finding(s) in files within the scanned codebase. The following "conditions" are used for defining the rule + +* `path` of the file within the sources (defined as a regular expression; matches to `files[].path` in the scancode json file) +* `oldCopyright` the found copyright text to ignore (defined as a regular expression; matches to `files[].copyrights[].copyright` in the scancode json file) + +At least one of the conditions has to be defined. + +====== Copyright: REPLACE +This follows the above principles. It uses the same conditions as REMOVE and uses a parameter to define the copyright to use instead: + +Data: +* `newCopyright`: The copyright entry to use instead of the originally found copyright + +====== Copyright: ADD +Adding new copyrights is done by defining rules which add new copyright info (to the copyrights found in a source file) - or "on top level". + +Conditions: + +* `path` of the file within the sources (defined as a regular expression; if omitted the copyright will be applied on "top level"). +Note that it is again only possible to add copyrigts to paths which are listed in the scancode json + +Data: + +* `newCopyright`: the copyright string to add + +===== Redefining all licenses / copyrights of a component + +Instead of curating license / copyrights on a "per finding" level as given above it is alternatively possible to completely replace the list of found licenses and/or copyrights with a new list. + +IMPORTANT: Up to version 1.23.0 this was the only way of doing license / copyright curations. Use of this way of curating data is still possible but discouraged and might be deprecated/removed soon. + +The file `output/curations.yaml` looks as follows when doing curations this way: + [source,yaml] ---- artifacts: @@ -1596,6 +1710,33 @@ artifacts: <10> A single path prefix. All scanned files starting with this path prefix are excluded from the Scancode information. <11> Further packages to follow. +===== Hierarchical definition of rules + +Different version of a package/component or even different packages/components within the same namespace often require mostly the same curations to be applied. To avoid being forced to redefine curations for every single version it is possible to define curations by just specifying a prefix part in the name attribute. + +Example of available levels/prefixes for `pkg:/maven/ch.qos.logback/logback-classic@1.2.3` + +* `pkg` +* `pkg/maven` +* `pkg/maven/ch` +* `pkg/maven/ch/qos` +* `pkg/maven/ch/qos/logback` +* `pkg/maven/ch/qos/logback/logback-classic` +* `pkg/maven/ch/qos/logback/logback-classic/1.2.3` + +The complete tree will be checked for curations. Any found curations will be merged + +* Attribute `name`: latest encountered in the hierarchy will be taken +* Attribute `note` will be joined using delimiter " / " +* Attribute `url`: latest encountered in the hierarchy will be taken +* Attribute `copyrights` (old style of curations): Lists will be merged +* Attribute `licenses` (old style of curations): Lists will be merged +* Attribute `excludedPaths`: Lists will be merged +* Attribute `licenseCurations`: License curation rule lists (REMOVE/REPLACE/ADD) will be merged; order is more specific ones first; when evaluating for a specific license finding in the scancode json only the first matching curation rule will be taken. +* Attribute `copyrightCurations`: Copyright curation rule lists (REMOVE/REPLACE/ADD) will be merged; order is more specific ones first; when evaluating for a specific copyright finding in the scancode json only the first matching curation rule will be taken. + +The resulting curation will then be applied to the scancode data of the component. + ==== Decision table rules As for license information obtained from the Readers the license information from ScanCode can also be altered using decision table rules. A new attribute `origin` was introduced in the `RawLicense` entity as well as condition field in decision table `LicenseAssignmentV2*.xls/csv`. The `origin` attribute in `Rawlicense` either contains the string `scancode` if the license information came from ScanCode or it contains the (lowercase) class name of the used Reader. @@ -1758,6 +1899,7 @@ Changes in 1.24.0:: * https://github.com/devonfw/solicitor/issues/263: Some features were pushed from deprecation stage 1 to stage 2, which means they do not longer work with default configuration: repoType attribute, npm and npm-license-crawler readers, REGEX prefix notation, LicenseAssignmentProject.xls. See <> for details. * https://github.com/devonfw/solicitor/pull/265: Added some license name mappings. * https://github.com/devonfw/solicitor/pull/266: Improve correlation of records (diff processing, see <>) for aggregated inventory report `OSS-Inventory_aggregated_*.xlsx`. +* https://github.com/devonfw/solicitor/issues/267: Introduce more fine grained and hierarchical curations of licenses and copyrights. See <>. Changes in 1.23.0:: * https://github.com/devonfw/solicitor/issues/255: Deprecate LicenseUrl guessing. diff --git a/solicitor.dict b/solicitor.dict index 702356ea..93f99e4f 100644 --- a/solicitor.dict +++ b/solicitor.dict @@ -32,6 +32,7 @@ SBOMs scancode sha SOLI +spdx sql Unlicense uri From 7f25bc76ee88397b419b08888d6e30e72e444a94 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:59:21 +0200 Subject: [PATCH 129/139] Replaced usage of List.of as this is not available (in yet still supported) JDK 8 (#270) --- ...tractHierarchicalCurationProviderTest.java | 4 +-- .../curation/CurationUtilTest.java | 30 ++++++++++--------- documentation/master-solicitor.asciidoc | 3 ++ 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProviderTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProviderTest.java index e78481d5..d691eb17 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProviderTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/AbstractHierarchicalCurationProviderTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.List; +import java.util.Collections; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -161,7 +161,7 @@ protected ComponentInfoCuration fetchCurationFromRepository(String effectiveCura CopyrightCuration cc = new CopyrightCuration(); cc.setOperation(CurationOperation.ADD); cc.setNewCopyright("Copyright " + effectiveCurationDataSelector + " " + pathFragmentWithinRepo); - cic.setCopyrightCurations(List.of(cc)); + cic.setCopyrightCurations(Collections.singletonList(cc)); return cic; } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java index efa4b193..ad0e3791 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java @@ -6,6 +6,8 @@ import java.io.FileInputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; @@ -63,27 +65,27 @@ void testMerge() { curationFirst.setName("firstName"); curationFirst.setNote("firstNote"); curationFirst.setUrl("firstUrl"); - curationFirst.setExcludedPaths(List.of("first path")); - curationFirst.setCopyrights(List.of("first copyright 1", "first copyright 2")); + curationFirst.setExcludedPaths(Collections.singletonList("first path")); + curationFirst.setCopyrights(Arrays.asList(new String[] { "first copyright 1", "first copyright 2" })); LicenseInfoCuration licFirst = new LicenseInfoCuration(); - curationFirst.setLicenses(List.of(licFirst)); + curationFirst.setLicenses(Collections.singletonList(licFirst)); CopyrightCuration ccFirst = new CopyrightCuration(); - curationFirst.setCopyrightCurations(List.of(ccFirst)); + curationFirst.setCopyrightCurations(Collections.singletonList(ccFirst)); LicenseCuration lcFirst = new LicenseCuration(); - curationFirst.setLicenseCurations(List.of(lcFirst)); + curationFirst.setLicenseCurations(Collections.singletonList(lcFirst)); ComponentInfoCuration curationSecond = new ComponentInfoCuration(); curationSecond.setName("secondName"); curationSecond.setNote("secondNote"); curationSecond.setUrl("secondUrl"); - curationSecond.setExcludedPaths(List.of("second path")); - curationSecond.setCopyrights(List.of("second copyright 1", "second copyright 2")); + curationSecond.setExcludedPaths(Collections.singletonList("second path")); + curationSecond.setCopyrights(Arrays.asList(new String[] { "second copyright 1", "second copyright 2" })); LicenseInfoCuration licSecond = new LicenseInfoCuration(); - curationSecond.setLicenses(List.of(licSecond)); + curationSecond.setLicenses(Collections.singletonList(licSecond)); CopyrightCuration ccSecond = new CopyrightCuration(); - curationSecond.setCopyrightCurations(List.of(ccSecond)); + curationSecond.setCopyrightCurations(Collections.singletonList(ccSecond)); LicenseCuration lcSecond = new LicenseCuration(); - curationSecond.setLicenseCurations(List.of(lcSecond)); + curationSecond.setLicenseCurations(Collections.singletonList(lcSecond)); ComponentInfoCuration merged = CurationUtil.merge(curationFirst, curationSecond); assertEquals("secondName", merged.getName()); @@ -91,10 +93,10 @@ void testMerge() { assertTrue(merged.getNote().contains("/")); assertTrue(merged.getNote().endsWith("firstNote")); assertEquals("secondUrl", merged.getUrl()); - assertTrue(merged.getCopyrights() - .equals(List.of("second copyright 1", "second copyright 2", "first copyright 1", "first copyright 2"))); - assertTrue(merged.getLicenses().equals(List.of(licSecond, licFirst))); - assertTrue(merged.getExcludedPaths().equals(List.of("second path", "first path"))); + assertTrue(merged.getCopyrights().equals(Arrays.asList( + new String[] { "second copyright 1", "second copyright 2", "first copyright 1", "first copyright 2" }))); + assertTrue(merged.getLicenses().equals(Arrays.asList(new LicenseInfoCuration[] { licSecond, licFirst }))); + assertTrue(merged.getExcludedPaths().equals(Arrays.asList(new String[] { "second path", "first path" }))); assertTrue(merged.getLicenseCurations().equals(List.of(lcSecond, lcFirst))); assertTrue(merged.getCopyrightCurations().equals(List.of(ccSecond, ccFirst))); } diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 5582dff3..17955a09 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1895,6 +1895,9 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.24.1:: +* https://github.com/devonfw/solicitor/pull/270: Fixed an incompatibility with JDK 8. + Changes in 1.24.0:: * https://github.com/devonfw/solicitor/issues/263: Some features were pushed from deprecation stage 1 to stage 2, which means they do not longer work with default configuration: repoType attribute, npm and npm-license-crawler readers, REGEX prefix notation, LicenseAssignmentProject.xls. See <> for details. * https://github.com/devonfw/solicitor/pull/265: Added some license name mappings. From 8a6cfa744d1f7b3364ebc080c1af57222d789e38 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:03:37 +0200 Subject: [PATCH 130/139] Replaced List.of which were missed in the first round (#271) --- .../solicitor/componentinfo/curation/CurationUtilTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java index ad0e3791..21311a5d 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/curation/CurationUtilTest.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; -import java.util.List; import org.junit.jupiter.api.Test; @@ -97,7 +96,7 @@ void testMerge() { new String[] { "second copyright 1", "second copyright 2", "first copyright 1", "first copyright 2" }))); assertTrue(merged.getLicenses().equals(Arrays.asList(new LicenseInfoCuration[] { licSecond, licFirst }))); assertTrue(merged.getExcludedPaths().equals(Arrays.asList(new String[] { "second path", "first path" }))); - assertTrue(merged.getLicenseCurations().equals(List.of(lcSecond, lcFirst))); - assertTrue(merged.getCopyrightCurations().equals(List.of(ccSecond, ccFirst))); + assertTrue(merged.getLicenseCurations().equals(Arrays.asList(new LicenseCuration[] { lcSecond, lcFirst }))); + assertTrue(merged.getCopyrightCurations().equals(Arrays.asList(new CopyrightCuration[] { ccSecond, ccFirst }))); } } From 9cc9be6a9a345ad439872614ff1b8df4d1d409cf Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:14:00 +0200 Subject: [PATCH 131/139] Update Releasenotes --- documentation/master-solicitor.asciidoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 17955a09..4570a2d2 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1895,10 +1895,13 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.24.2:: +* https://github.com/devonfw/solicitor/pull/271: Fixed an incompatibility with JDK 8. + Changes in 1.24.1:: * https://github.com/devonfw/solicitor/pull/270: Fixed an incompatibility with JDK 8. -Changes in 1.24.0:: +Changes in 1.24.2:: * https://github.com/devonfw/solicitor/issues/263: Some features were pushed from deprecation stage 1 to stage 2, which means they do not longer work with default configuration: repoType attribute, npm and npm-license-crawler readers, REGEX prefix notation, LicenseAssignmentProject.xls. See <> for details. * https://github.com/devonfw/solicitor/pull/265: Added some license name mappings. * https://github.com/devonfw/solicitor/pull/266: Improve correlation of records (diff processing, see <>) for aggregated inventory report `OSS-Inventory_aggregated_*.xlsx`. From e59deb5b745ba051b153fff0e8f2e4e93136c35b Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 11 Jun 2024 16:00:46 +0200 Subject: [PATCH 132/139] Version 1.24.2 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 5da36a5c..ab7aadb8 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.24.0-SNAPSHOT + 1.24.2 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index b426a53c..5a8e09ec 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.24.0-SNAPSHOT + 1.24.2 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 402ee609..850b4f13 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.24.0-SNAPSHOT + 1.24.2 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index 5e6e036b..c4ea5c47 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.24.0-SNAPSHOT + 1.24.2 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index e5d1a4bd..899c1203 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.24.0-SNAPSHOT + 1.24.2 pom Solicitor Aggregator From b2177ac423edacedf79b95e2c27e6754047eb11c Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Tue, 11 Jun 2024 16:31:33 +0200 Subject: [PATCH 133/139] Set version to SNAPSHOT of next minor release (1.25.0-SNAPSHOT) --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/master-solicitor.asciidoc | 2 ++ documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index ab7aadb8..00df40b0 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.24.2 + 1.25.0-SNAPSHOT Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 5a8e09ec..0f725377 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.24.2 + 1.25.0-SNAPSHOT Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 4570a2d2..465b24d2 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1895,6 +1895,8 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes +Changes in 1.25.0:: + Changes in 1.24.2:: * https://github.com/devonfw/solicitor/pull/271: Fixed an incompatibility with JDK 8. diff --git a/documentation/pom.xml b/documentation/pom.xml index 850b4f13..04fb24a0 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.24.2 + 1.25.0-SNAPSHOT pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index c4ea5c47..d2c61edb 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.24.2 + 1.25.0-SNAPSHOT Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 899c1203..57ec5b16 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.24.2 + 1.25.0-SNAPSHOT pom Solicitor Aggregator From e2a683cf69f4bcf263fb80c4b6100c49be11aca8 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Wed, 26 Jun 2024 13:15:03 +0200 Subject: [PATCH 134/139] Corrected an exception message text --- .../componentinfo/curation/SingleFileCurationProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java index a9f38d29..f82a4c92 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/curation/SingleFileCurationProvider.java @@ -93,7 +93,7 @@ protected ComponentInfoCuration fetchCurationFromRepository(String effectiveCura } } catch (IOException e) { - throw new ComponentInfoAdapterException("Could not read Curations JSON", e); + throw new ComponentInfoAdapterException("Could not read Curations YAML", e); } } return foundCuration; From fdba053456825b184c3243dfa982fb4ce581e274 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:16:49 +0200 Subject: [PATCH 135/139] Do not read content files within scancode adapter if they exceed a given size (1 Mio bytes) (#278) --- .../tools/solicitor/common/LogMessages.java | 4 +- .../FileScancodeRawComponentInfoProvider.java | 18 ++++ .../src/main/resources/application.properties | 4 + ...ScancodeRawComponentInfoProviderTests.java | 84 +++++++++++++++++++ documentation/files/application.properties | 4 + documentation/master-solicitor.asciidoc | 19 +++-- 6 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProviderTests.java diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 0b4afff1..6cd91c39 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -106,7 +106,9 @@ public enum LogMessages { "When reading yarn license info from file '{}' there was at least one patched package encountered. Processing only the base package, not the patched version."), // FAILED_READING_FILE(71, "Reading file '{}' failed"), // EMPTY_PACKAGE_URL(72, "The package URL is null or empty."), // - EMPTY_PACKAGE_PATH(73, "The package path is null or empty."); + EMPTY_PACKAGE_PATH(73, "The package path is null or empty."), // + CONTENT_FILE_TOO_LARGE(74, + "The size of the content file '{}' is '{}' (max. allowed is '{}'). Reading will be skipped."); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java index c4397c60..dba3f579 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProvider.java @@ -38,6 +38,8 @@ public class FileScancodeRawComponentInfoProvider implements ScancodeRawComponen private String repoBasePath; + private long maxContentFileSize = 1000000L; // set this to the default even if spring is not used + private AllKindsPackageURLHandler packageURLHandler; /** @@ -63,6 +65,17 @@ public void setRepoBasePath(String repoBasePath) { this.repoBasePath = repoBasePath; } + /** + * Sets the maximum content file size. Files larger that this will not be processed to avoid OOM. + * + * @param maxContentFileSize new limit. + */ + @Value("${solicitor.scancode.max-content-file-size:1000000}") + public void setMaxContentFileSize(long maxContentFileSize) { + + this.maxContentFileSize = maxContentFileSize; + } + /** * Retrieve the {@link ScancodeRawComponentInfo} for the package given by its PackageURL. * @@ -191,6 +204,11 @@ public String retrieveContent(String packageUrl, String fileUri) { String fullFilePathAndName = IOHelper.secureFilePath(this.repoBasePath, this.packageURLHandler.pathFor(packageUrl), SOURCES_DIR, relativeFilePathAndName); File file = new File(fullFilePathAndName); + long fileSize = file.length(); + if (fileSize > this.maxContentFileSize) { + LOG.info(LogMessages.CONTENT_FILE_TOO_LARGE.msg(), fullFilePathAndName, fileSize, this.maxContentFileSize); + return null; + } try (InputStream is = new FileInputStream(file); Scanner s = new Scanner(is)) { s.useDelimiter("\\A"); String result = s.hasNext() ? s.next() : ""; diff --git a/core/src/main/resources/application.properties b/core/src/main/resources/application.properties index 901ec800..ffd53a77 100644 --- a/core/src/main/resources/application.properties +++ b/core/src/main/resources/application.properties @@ -64,6 +64,10 @@ solicitor.scancode.curations-filename=output/curations.yaml solicitor.scancode.repo-base-path=output/Source # list of patterns of found licenses which shall set the dataStatus to WITH_ISSUES solicitor.scancode.issuelistpatterns=.*unknown.* +# the maximum file size which is processed when retrieving license texts and notice file content +# from the source files of the package. This limit prevents huge memory consumption which might cause possible +# stability problems. +#solicitor.scancode.solicitor.scancode.max-content-file-size=1000000 # The curationDataSelector value to use when accessing curation data. # You can change its value to select a specific curation data source (if the implementation supports this). diff --git a/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProviderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProviderTests.java new file mode 100644 index 00000000..a8edbb18 --- /dev/null +++ b/core/src/test/java/com/devonfw/tools/solicitor/componentinfo/scancode/FileScancodeRawComponentInfoProviderTests.java @@ -0,0 +1,84 @@ +package com.devonfw.tools.solicitor.componentinfo.scancode; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.devonfw.tools.solicitor.common.packageurl.AllKindsPackageURLHandler; + +/** + * This class contains JUnit test methods for the {@link FileScancodeRawComponentInfoProvider} class. + */ +public class FileScancodeRawComponentInfoProviderTests { + + // the object under test + FileScancodeRawComponentInfoProvider fileScancodeRawComponentInfoProvider; + + @BeforeEach + public void setup() { + + AllKindsPackageURLHandler packageURLHandler = Mockito.mock(AllKindsPackageURLHandler.class); + + Mockito.when(packageURLHandler.pathFor("pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0")) + .thenReturn("pkg/maven/com/devonfw/tools/test-project-for-deep-license-scan/0.1.0"); + + this.fileScancodeRawComponentInfoProvider = new FileScancodeRawComponentInfoProvider(packageURLHandler); + this.fileScancodeRawComponentInfoProvider.setRepoBasePath("src/test/resources/scancodefileadapter/Source/repo"); + + } + + /** + * Test the {@link FileScancodeRawComponentInfoProvider#retrieveContent(String, String)} method for a file which does + * not exceed the given default size limit (which should be 1 Mio Bytes). + */ + @Test + public void testRetrieveContentFileSizeOkForDefault() { + + // when + String fileContent = this.fileScancodeRawComponentInfoProvider.retrieveContent( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "pkgcontent:/NOTICE.txt"); + + // then + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", fileContent); + } + + /** + * Test the {@link FileScancodeRawComponentInfoProvider#retrieveContent(String, String)} method for a file which does + * not exceed the given size limit. + */ + @Test + public void testRetrieveContentFileSizeOk() { + + // given + this.fileScancodeRawComponentInfoProvider.setMaxContentFileSize(66); + + // when + String fileContent = this.fileScancodeRawComponentInfoProvider.retrieveContent( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "pkgcontent:/NOTICE.txt"); + + // then + assertEquals("This is a dummy notice file for testing. Code is under Apache-2.0.", fileContent); + } + + /** + * Test the {@link FileScancodeRawComponentInfoProvider#retrieveContent(String, String)} method for a file which does + * exceed the given size limit. + */ + @Test + public void testRetrieveContentFileSizeNotOk() { + + // given + this.fileScancodeRawComponentInfoProvider.setMaxContentFileSize(65); + + // when + String fileContent = this.fileScancodeRawComponentInfoProvider.retrieveContent( + "pkg:maven/com.devonfw.tools/test-project-for-deep-license-scan@0.1.0", "pkgcontent:/NOTICE.txt"); + + // then + assertNull(fileContent); + } + +} diff --git a/documentation/files/application.properties b/documentation/files/application.properties index 901ec800..ffd53a77 100644 --- a/documentation/files/application.properties +++ b/documentation/files/application.properties @@ -64,6 +64,10 @@ solicitor.scancode.curations-filename=output/curations.yaml solicitor.scancode.repo-base-path=output/Source # list of patterns of found licenses which shall set the dataStatus to WITH_ISSUES solicitor.scancode.issuelistpatterns=.*unknown.* +# the maximum file size which is processed when retrieving license texts and notice file content +# from the source files of the package. This limit prevents huge memory consumption which might cause possible +# stability problems. +#solicitor.scancode.solicitor.scancode.max-content-file-size=1000000 # The curationDataSelector value to use when accessing curation data. # You can change its value to select a specific curation data source (if the implementation supports this). diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 465b24d2..9abd2cfa 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1307,7 +1307,7 @@ The Generic Excel Writer exists purely for debugging purposes. This writer write "APPLICATIONCOMPONENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_applicationcomponents.sql", "LICENSE" : "classpath:com/devonfw/tools/solicitor/sql/allden_normalizedlicenses.sql", "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql", - ... + ... } } ] @@ -1574,7 +1574,7 @@ artifacts: - name: pkg/npm/@somescope/somepackage/1.2.3 <1> url: https://github.com/foo/bar <2> licenseCurations: <3> - - operation: REMOVE + - operation: REMOVE path: "sources/package/readme.md" ruleIdentifier: "proprietary-license_unknown_13.RULE" matchedText: ".* to be paid .*" @@ -1603,8 +1603,8 @@ artifacts: <7> Further packages to follow. ===== Rules for curating licenses -Curating licenses is done by REMOVING (i.e. ignoring) specific license findings from ScanCode, by REPLACING the detected license with another one or by ADDING license findings either to specific files or on top level (not related to specific file of the package sources). -In addition to the conditions/data which is specific for any of the below described operations it is always possible to define a comment which is intended to be included in any audit trail log for documentation purposes (not yet used/implemented). +Curating licenses is done by REMOVING (i.e. ignoring) specific license findings from ScanCode, by REPLACING the detected license with another one or by ADDING license findings either to specific files or on top level (not related to specific file of the package sources). +In addition to the conditions/data which is specific for any of the below described operations it is always possible to define a comment which is intended to be included in any audit trail log for documentation purposes (not yet used/implemented). ====== Licenses: REMOVE @@ -1624,7 +1624,7 @@ Instead of removing licenses (ignoring the finding) they might be replaced with Data: * `newLicense` is the key / id of the license to use instead (replacing `files[].licenses[].spdx_license_key`) -* `url` is the url pointing to the license text +* `url` is the url pointing to the license text At least one of the two parameters has to be set. @@ -1658,14 +1658,14 @@ At least one of the conditions has to be defined. This follows the above principles. It uses the same conditions as REMOVE and uses a parameter to define the copyright to use instead: Data: -* `newCopyright`: The copyright entry to use instead of the originally found copyright +* `newCopyright`: The copyright entry to use instead of the originally found copyright ====== Copyright: ADD Adding new copyrights is done by defining rules which add new copyright info (to the copyrights found in a source file) - or "on top level". Conditions: -* `path` of the file within the sources (defined as a regular expression; if omitted the copyright will be applied on "top level"). +* `path` of the file within the sources (defined as a regular expression; if omitted the copyright will be applied on "top level"). Note that it is again only possible to add copyrigts to paths which are listed in the scancode json Data: @@ -1712,9 +1712,9 @@ artifacts: ===== Hierarchical definition of rules -Different version of a package/component or even different packages/components within the same namespace often require mostly the same curations to be applied. To avoid being forced to redefine curations for every single version it is possible to define curations by just specifying a prefix part in the name attribute. +Different version of a package/component or even different packages/components within the same namespace often require mostly the same curations to be applied. To avoid being forced to redefine curations for every single version it is possible to define curations by just specifying a prefix part in the name attribute. -Example of available levels/prefixes for `pkg:/maven/ch.qos.logback/logback-classic@1.2.3` +Example of available levels/prefixes for `pkg:/maven/ch.qos.logback/logback-classic@1.2.3` * `pkg` * `pkg/maven` @@ -1896,6 +1896,7 @@ Spring beans implementing this interface will be called at certain points in the [appendix] == Release Notes 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. Changes in 1.24.2:: * https://github.com/devonfw/solicitor/pull/271: Fixed an incompatibility with JDK 8. From 3abdf85defd0e320f58512a92a2d18f5ebf12b37 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:30:41 +0200 Subject: [PATCH 136/139] Fixed 2 bugs in user guide --- documentation/master-solicitor.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 9abd2cfa..01a0073d 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -1904,7 +1904,7 @@ Changes in 1.24.2:: Changes in 1.24.1:: * https://github.com/devonfw/solicitor/pull/270: Fixed an incompatibility with JDK 8. -Changes in 1.24.2:: +Changes in 1.24.0:: * https://github.com/devonfw/solicitor/issues/263: Some features were pushed from deprecation stage 1 to stage 2, which means they do not longer work with default configuration: repoType attribute, npm and npm-license-crawler readers, REGEX prefix notation, LicenseAssignmentProject.xls. See <> for details. * https://github.com/devonfw/solicitor/pull/265: Added some license name mappings. * https://github.com/devonfw/solicitor/pull/266: Improve correlation of records (diff processing, see <>) for aggregated inventory report `OSS-Inventory_aggregated_*.xlsx`. @@ -1982,7 +1982,7 @@ Changes in 1.10.0:: Changes in 1.9.0:: * https://github.com/devonfw/solicitor/issues/171: Multiple improvements in processing of ScanCode results. -* https://github.com/devonfw/solicitor/issues/167: Fixed a bug which prevented license URLs given in scancode curations (see <>) to be resolved properly when they point to the local file system (starting with `file:`). +* https://github.com/devonfw/solicitor/issues/167: Fixed a bug which prevented license URLs given in scancode curations (see <>) to be resolved properly when they point to the local file system (starting with `file:`). Changes in 1.8.1:: From 6276bfa0b830a73909452c37e6b82244d616ccb3 Mon Sep 17 00:00:00 2001 From: duph97 Date: Thu, 25 Jul 2024 17:25:38 +0200 Subject: [PATCH 137/139] Bugfix/csvreader missing packageurl (#275) * 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 * Shorten if condition * Set packageType=null in unit tests * Update typo in documentation/master-solicitor.asciidoc * remove mockito dependency from POM as it is already included via spring-boot-starter-test --------- Co-authored-by: chrimih <101123170+chrimih@users.noreply.github.com> Co-authored-by: ohecker <8004361+ohecker@users.noreply.github.com> --- .../devonfw/tools/solicitor/Solicitor.java | 2 +- .../tools/solicitor/SolicitorSetup.java | 22 ++++ .../tools/solicitor/common/LogMessages.java | 5 +- .../tools/solicitor/config/ConfigFactory.java | 1 + .../tools/solicitor/config/ReaderConfig.java | 22 ++++ .../tools/solicitor/reader/Reader.java | 3 +- .../tools/solicitor/reader/csv/CsvReader.java | 52 +++++++- .../reader/cyclonedx/CyclonedxReader.java | 2 +- .../solicitor/reader/gradle/GradleReader.java | 2 +- .../reader/gradle/GradleReader2.java | 2 +- .../solicitor/reader/maven/MavenReader.java | 2 +- .../NpmLicenseCheckerReader.java | 2 +- .../NpmLicenseCrawlerReader.java | 2 +- .../tools/solicitor/reader/ort/OrtReader.java | 2 +- .../reader/piplicenses/PipLicensesReader.java | 2 +- .../solicitor/reader/yarn/YarnReader.java | 2 +- .../reader/yarnmodern/YarnModernReader.java | 2 +- .../solicitor/reader/csv/CsvReaderTests.java | 118 +++++++++++++++++- .../cyclonedx/CyclonedxReaderTests.java | 8 +- .../reader/gradle/GradleReader2Tests.java | 2 +- .../reader/gradle/GradleReaderTests.java | 2 +- .../reader/maven/MavenReaderTests.java | 4 +- .../NpmLicenseCheckerReaderTests.java | 2 +- .../NpmLicenseCrawlerReaderTests.java | 2 +- .../solicitor/reader/ort/OrtReaderTests.java | 2 +- .../reader/piplicenses/PipReaderTests.java | 2 +- .../reader/yarn/YarnReaderTests.java | 2 +- .../yarnmodern/YarnModernReaderTests.java | 2 +- core/src/test/resources/csvlicenses_npm.csv | 3 + core/src/test/resources/csvlicenses_pypi.csv | 3 + documentation/master-solicitor.asciidoc | 26 ++-- 31 files changed, 265 insertions(+), 40 deletions(-) create mode 100644 core/src/test/resources/csvlicenses_npm.csv create mode 100644 core/src/test/resources/csvlicenses_pypi.csv diff --git a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java index 0be13e4f..6723b670 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/Solicitor.java @@ -164,7 +164,7 @@ private void readInventory() { Reader reader = this.readerFactory.readerFor(readerSetup.getType()); try { reader.readInventory(readerSetup.getType(), readerSetup.getSource(), readerSetup.getApplication(), - readerSetup.getUsagePattern(), readerSetup.getRepoType(), readerSetup.getConfiguration()); + readerSetup.getUsagePattern(), readerSetup.getRepoType(), readerSetup.getPackageType(), readerSetup.getConfiguration()); } catch (SolicitorRuntimeException sre) { if (this.tolerateMissingInput && sre.getCause() instanceof FileNotFoundException) { Application app = readerSetup.getApplication(); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/SolicitorSetup.java b/core/src/main/java/com/devonfw/tools/solicitor/SolicitorSetup.java index 1bebaeaa..c561eb66 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/SolicitorSetup.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/SolicitorSetup.java @@ -35,6 +35,8 @@ public static class ReaderSetup { private UsagePattern usagePattern; private String repoType; + + private String packageType; private Map configuration; @@ -88,6 +90,15 @@ public String getRepoType() { return this.repoType; } + /** + * This method gets the field packageType. + * + * @return the field packageType + */ + public String getPackageType() { + + return this.packageType; + } /** * This method gets the field configuration. * @@ -157,8 +168,19 @@ public void setRepoType(String repoType) { this.repoType = repoType; } + + /** + * This method sets the field packageType. + * + * @param packageType the new value of the field packageType + */ + public void setPackageType(String packageType) { + + this.packageType = packageType; + } } + private String engagementName; private List readerSetups = new ArrayList<>(); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java index 6cd91c39..432fbd86 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/common/LogMessages.java @@ -107,7 +107,10 @@ public enum LogMessages { FAILED_READING_FILE(71, "Reading file '{}' failed"), // EMPTY_PACKAGE_URL(72, "The package URL is null or empty."), // EMPTY_PACKAGE_PATH(73, "The package path is null or empty."), // - CONTENT_FILE_TOO_LARGE(74, + EMPTY_PACKAGE_TYPE(74, "The package type is null or empty."), // + UNKNOWN_PACKAGE_TYPE(75, + "The CSV file contains packageType '{}' which is not supported and will be ignored. Solicitor reports might be incomplete"), // + CONTENT_FILE_TOO_LARGE(76, "The size of the content file '{}' is '{}' (max. allowed is '{}'). Reading will be skipped."); private final String message; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java b/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java index e5a60228..37371ba9 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/config/ConfigFactory.java @@ -132,6 +132,7 @@ public ModelRoot createConfig(String url) { + "https://github.com/devonfw/solicitor/issues/263"); } rs.setRepoType(rc.getRepoType()); + rs.setPackageType(rc.getPackageType()); rs.setConfiguration(rc.getConfiguration()); rs = resolvePlaceholdersInReader(rs, placeHolderMap); this.solicitorSetup.getReaderSetups().add(rs); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/config/ReaderConfig.java b/core/src/main/java/com/devonfw/tools/solicitor/config/ReaderConfig.java index 3ce7d926..442b29f0 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/config/ReaderConfig.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/config/ReaderConfig.java @@ -27,6 +27,9 @@ public class ReaderConfig { @JsonProperty private String repoType; + + @JsonProperty + private String packageType; @JsonProperty private Map configuration; @@ -49,6 +52,16 @@ public String getRepoType() { return this.repoType; } + + /** + * This method gets the field packageType. + * + * @return the field packageType + */ + public String getPackageType() { + + return this.packageType; + } /** * This method gets the field configuration. @@ -99,7 +112,16 @@ public void setRepoType(String repoType) { this.repoType = repoType; } + + /** + * This method sets the field packageType. + * + * @param packageType the new value of the field packageType + */ + public void setPackageType(String packageType) { + this.packageType = packageType; + } /** * This method sets the field configuration. * diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/Reader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/Reader.java index 0f343288..84fe4652 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/Reader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/Reader.java @@ -36,9 +36,10 @@ public interface Reader { * @param application all read {@link ApplicationComponent} need to be linked with this {@link Application} * @param usagePattern the {@link UsagePattern} which applies for all read {@link ApplicationComponent}s * @param repoType the type of Repository to download the sources from + * @param packageType the packageType to create the packageUrl * @param configuration optional configuration settings for readers */ public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration); + String repoType, String packageType, Map configuration); } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/csv/CsvReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/csv/CsvReader.java index 28f85e7c..4b41aeee 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/csv/CsvReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/csv/CsvReader.java @@ -14,13 +14,19 @@ import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVRecord; import org.springframework.stereotype.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.devonfw.tools.solicitor.common.LogMessages; +import com.devonfw.tools.solicitor.common.PackageURLHelper; import com.devonfw.tools.solicitor.common.SolicitorRuntimeException; import com.devonfw.tools.solicitor.model.inventory.ApplicationComponent; import com.devonfw.tools.solicitor.model.masterdata.Application; import com.devonfw.tools.solicitor.model.masterdata.UsagePattern; import com.devonfw.tools.solicitor.reader.AbstractReader; import com.devonfw.tools.solicitor.reader.Reader; +import com.devonfw.tools.solicitor.reader.maven.MavenReader; +import com.github.packageurl.PackageURL; /** * A {@link Reader} for files in CSV format. @@ -46,6 +52,8 @@ @Component public class CsvReader extends AbstractReader implements Reader { + private Logger logger = LoggerFactory.getLogger(CsvReader.class); // not static final for testing + // purposes /** * The supported type of this {@link Reader}. @@ -62,7 +70,7 @@ public Set getSupportedTypes() { /** {@inheritDoc} */ @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { int components = 0; int licenses = 0; @@ -214,6 +222,7 @@ public void readInventory(String type, String sourceUrl, Application application appComponent.setVersion(version); appComponent.setUsagePattern(usagePattern); appComponent.setRepoType(repoType); + appComponent.setPackageUrl(getPackageURL(packageType, groupId, artifactId, version)); // merge ApplicationComponentImpl with same key if they appear // on @@ -248,6 +257,8 @@ public void readInventory(String type, String sourceUrl, Application application appComponent.setVersion(record.get(2)); appComponent.setUsagePattern(usagePattern); appComponent.setRepoType(repoType); + appComponent.setPackageUrl(getPackageURL(packageType, record.get(0), record.get(1), record.get(2))); + // merge ApplicationComponentImpl with same key if they appear // on // subsequent lines (multilicensing) @@ -275,4 +286,43 @@ public void readInventory(String type, String sourceUrl, Application application } + /** + * Call the appropriate {@link PackageURLHelper} method to create a packageURL. Returns null if no + * {@link PackageURLHelper} exists for the packageType. + * + * @param packageType the package type + * @param groupId the groupId if available + * @param artifactId the artifactId + * @param version the version + * @return the created PackageURL + */ + public String getPackageURL(String packageType, String groupId, String artifactId, String version) { + + if (packageType == null || packageType.isEmpty()) { + this.logger.warn(LogMessages.EMPTY_PACKAGE_TYPE.msg(), packageType); + return null; + } + switch (packageType) { + case "maven": + return PackageURLHelper.fromMavenCoordinates(groupId, artifactId, version).toString(); + case "npm": + return PackageURLHelper.fromNpmPackageNameAndVersion(artifactId, version).toString(); + case "pypi": + return PackageURLHelper.fromPyPICoordinates(artifactId, version).toString(); + default: + this.logger.warn(LogMessages.UNKNOWN_PACKAGE_TYPE.msg(), packageType); + return null; + } + } + + /** + * Sets the logger. Available for testing purposes only. + * + * @param logger the logger + */ + void setLogger(Logger logger) { + + this.logger = logger; + } + } diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java index a95953a0..e6258cd2 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java @@ -55,7 +55,7 @@ public Set getSupportedTypes() { /** {@inheritDoc} */ @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { int componentCount = 0; int licenseCount = 0; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader.java index e589f6b0..f1d910c4 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader.java @@ -61,7 +61,7 @@ public Set getSupportedTypes() { /** {@inheritDoc} */ @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { this.deprecationChecker.check(false, "Use of Reader of type 'gradle' is deprecated, use 'gradle2' instead. See https://github.com/devonfw/solicitor/issues/58"); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader2.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader2.java index e7ed6cb5..006af338 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader2.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader2.java @@ -48,7 +48,7 @@ public Set getSupportedTypes() { /** {@inheritDoc} */ @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { int components = 0; int licenses = 0; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/maven/MavenReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/maven/MavenReader.java index ee48d2d7..094349f6 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/maven/MavenReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/maven/MavenReader.java @@ -59,7 +59,7 @@ public Set getSupportedTypes() { /** {@inheritDoc} */ @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { int components = 0; int licenses = 0; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java index df6ad64e..49136528 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReader.java @@ -48,7 +48,7 @@ public Set getSupportedTypes() { @SuppressWarnings("rawtypes") @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { int componentCount = 0; int licenseCount = 0; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java index 9ddb9be8..ff10ee41 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReader.java @@ -67,7 +67,7 @@ public Set getSupportedTypes() { /** {@inheritDoc} */ @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { this.deprecationChecker.check(false, "Use of Reader of type '" + SUPPORTED_TYPE diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java index db15b3a3..cb27e879 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/ort/OrtReader.java @@ -44,7 +44,7 @@ public Set getSupportedTypes() { @SuppressWarnings("rawtypes") @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { int componentCount = 0; int licenseCount = 0; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/piplicenses/PipLicensesReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/piplicenses/PipLicensesReader.java index bf903d50..cf335a2d 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/piplicenses/PipLicensesReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/piplicenses/PipLicensesReader.java @@ -44,7 +44,7 @@ public Set getSupportedTypes() { @SuppressWarnings("rawtypes") @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { int componentCount = 0; int licenseCount = 0; diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java index 625a7165..8a6e7bae 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarn/YarnReader.java @@ -48,7 +48,7 @@ public Set getSupportedTypes() { @SuppressWarnings("rawtypes") @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { String content = cutSourceJson(sourceUrl); diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReader.java index 1ca903b4..44423011 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReader.java @@ -55,7 +55,7 @@ public Set getSupportedTypes() { @SuppressWarnings("rawtypes") @Override public void readInventory(String type, String sourceUrl, Application application, UsagePattern usagePattern, - String repoType, Map configuration) { + String repoType, String packageType, Map configuration) { String content = readAndPreprocessJson(sourceUrl); diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/csv/CsvReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/csv/CsvReaderTests.java index e6567ee7..5c52174d 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/csv/CsvReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/csv/CsvReaderTests.java @@ -6,12 +6,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,6 +30,8 @@ public class CsvReaderTests { Application application; + private Logger logger; + public CsvReaderTests() { // configuration settings @@ -59,7 +64,7 @@ public CsvReaderTests() { csvr.setModelFactory(modelFactory); csvr.setInputStreamFactory(new FileInputStreamFactory()); csvr.readInventory("csv", "src/test/resources/csvlicenses.csv", this.application, UsagePattern.DYNAMIC_LINKING, - "maven", configuration); + "maven", "maven", configuration); } @@ -101,4 +106,115 @@ public void testFindLicense() { assertTrue(found); } + /** + * Tests if packageUrl for maven has been created. Reader runs with config. + */ + @Test + public void testFindMavenPackageUrl() { + + List lapc = this.application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("albireo") && ap.getPackageUrl().equals("pkg:maven/org.eclipse/albireo@0.0.3")) { + found = true; + break; + } + } + assertTrue(found); + + } + + /** + * Tests if packageUrl for npm has been created. Reader runs without config. + */ + @Test + public void testFindNpmPackageUrl() { + + Application application; + ModelFactory modelFactory = new ModelFactoryImpl(); + application = modelFactory.newApplication("testAppNpm", "0.0.0.TEST", "1.1.2111", "http://bla.com", "NPM"); + CsvReader csvr = new CsvReader(); + csvr.setModelFactory(modelFactory); + csvr.setInputStreamFactory(new FileInputStreamFactory()); + csvr.readInventory("csv", "src/test/resources/csvlicenses_npm.csv", application, UsagePattern.DYNAMIC_LINKING, + "npm", "npm", null); + + List lapc = application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("@babel/code-frame") + && ap.getPackageUrl().equals("pkg:npm/%40babel/code-frame@7.8.3")) { + found = true; + break; + } + } + assertTrue(found); + + } + + /** + * Tests if packageUrl for pypi has been created. Reader runs with config and swapped artifactId and version position. + */ + @Test + public void testFindPypiPackageUrl() { + + Application application; + ModelFactory modelFactory = new ModelFactoryImpl(); + application = modelFactory.newApplication("testAppPypi", "0.0.0.TEST", "1.1.2111", "http://bla.com", "Python"); + + // configuration settings + Map configuration = new HashMap(); + configuration.put("artifactId", "2"); + configuration.put("version", "1"); + configuration.put("delimiter", ";"); + + CsvReader csvr = new CsvReader(); + csvr.setModelFactory(modelFactory); + csvr.setInputStreamFactory(new FileInputStreamFactory()); + csvr.readInventory("csv", "src/test/resources/csvlicenses_pypi.csv", application, UsagePattern.DYNAMIC_LINKING, + "pypi", "pypi", configuration); + + List lapc = application.getApplicationComponents(); + boolean found = false; + for (ApplicationComponent ap : lapc) { + if (ap.getArtifactId().equals("python-dateutil") && ap.getPackageUrl().equals("pkg:pypi/python-dateutil@2.8.1")) { + found = true; + break; + } + } + assertTrue(found); + + } + + /** + * Tests if reader crashes with correct warn message if no packageType is given. Reader runs without config. + */ + @Test + public void testNullPackageUrl() { + + String expectedLogMessage = "The package type is null or empty"; + + Application application; + ModelFactory modelFactory = new ModelFactoryImpl(); + application = modelFactory.newApplication("testAppNull", "0.0.0.TEST", "1.1.2111", "http://bla.com", "Python"); + CsvReader csvr = new CsvReader(); + csvr.setModelFactory(modelFactory); + csvr.setInputStreamFactory(new FileInputStreamFactory()); + + this.logger = mock(Logger.class); + csvr.setLogger(this.logger); + + try { + csvr.readInventory("csv", "src/test/resources/csvlicenses_pypi.csv", application, UsagePattern.DYNAMIC_LINKING, + null, null, null); + } catch (Exception e) { + // Capture the log messages + ArgumentCaptor logEntry1 = ArgumentCaptor.forClass(String.class); + verify(logger).warn(logEntry1.capture()); + + assertEquals(expectedLogMessage, logEntry1.getValue()); + } + + } + } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java index 32667739..7e68c505 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java @@ -49,7 +49,7 @@ public void readMavenFileAndCheckSize() { this.cdxr.setInputStreamFactory(new FileInputStreamFactory()); this.cdxr.setDelegatingPackageURLHandler(this.delegatingPurlHandler); this.cdxr.readInventory("maven", "src/test/resources/mavensbom.json", application, UsagePattern.DYNAMIC_LINKING, - "cyclonedx", null); + "cyclonedx", null, null); LOG.info(application.toString()); assertEquals(32, application.getApplicationComponents().size()); @@ -85,7 +85,7 @@ public void readMavenFileAndCheckSizeNegative() { this.cdxr.setInputStreamFactory(new FileInputStreamFactory()); this.cdxr.setDelegatingPackageURLHandler(this.delegatingPurlHandler); this.cdxr.readInventory("maven", "src/test/resources/mavensbom.json", application, UsagePattern.DYNAMIC_LINKING, - "cyclonedx", null); + "cyclonedx", null, null); LOG.info(application.toString()); assertEquals(32, application.getApplicationComponents().size()); @@ -108,7 +108,7 @@ public void readMavenFileAndCheckSingleContentSize() { this.cdxr.setInputStreamFactory(new FileInputStreamFactory()); this.cdxr.setDelegatingPackageURLHandler(this.delegatingPurlHandler); this.cdxr.readInventory("maven", "src/test/resources/mavensbom.json", application, UsagePattern.DYNAMIC_LINKING, - "someRepoType", null); + "someRepoType", null, null); LOG.info(application.toString()); assertEquals(32, application.getApplicationComponents().size()); @@ -170,7 +170,7 @@ public void readNpmFileAndCheckSize() { this.cdxr.setInputStreamFactory(new FileInputStreamFactory()); this.cdxr.setDelegatingPackageURLHandler(this.delegatingPurlHandler); this.cdxr.readInventory("npm", "src/test/resources/npmsbom.json", application, UsagePattern.DYNAMIC_LINKING, - "cyclonedx", null); + "cyclonedx", null, null); LOG.info(application.toString()); assertEquals(74, application.getApplicationComponents().size()); diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader2Tests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader2Tests.java index dee7adcb..dfc04abd 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader2Tests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/gradle/GradleReader2Tests.java @@ -34,7 +34,7 @@ public GradleReader2Tests() { gr.setModelFactory(modelFactory); gr.setInputStreamFactory(new FileInputStreamFactory()); gr.readInventory("gradle2", "src/test/resources/licenseReport.json", this.application, UsagePattern.DYNAMIC_LINKING, - "maven", null); + "maven", null, null); } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/gradle/GradleReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/gradle/GradleReaderTests.java index 196fd071..6dd8ee81 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/gradle/GradleReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/gradle/GradleReaderTests.java @@ -43,7 +43,7 @@ public void check(boolean warnOnly, String detailsString) { gr.setModelFactory(modelFactory); gr.setInputStreamFactory(new FileInputStreamFactory()); gr.readInventory("gradle", "src/test/resources/licenseReport.json", this.application, UsagePattern.DYNAMIC_LINKING, - "maven", null); + "maven", null, null); } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/maven/MavenReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/maven/MavenReaderTests.java index 13f492c1..ac89fed3 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/maven/MavenReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/maven/MavenReaderTests.java @@ -39,7 +39,7 @@ public void readFileAndCheckSize() { mr.setModelFactory(modelFactory); mr.setInputStreamFactory(new FileInputStreamFactory()); mr.readInventory("maven", "src/test/resources/licenses_sample.xml", application, UsagePattern.DYNAMIC_LINKING, - "maven", null); + "maven", null, null); LOG.info(application.toString()); assertEquals(95, application.getApplicationComponents().size()); @@ -74,7 +74,7 @@ public void testProtectionAgainstXxe() { try { mr.readInventory("maven", "src/test/resources/licenses_sample_with_doctype.xml", application, - UsagePattern.DYNAMIC_LINKING, "maven", null); + UsagePattern.DYNAMIC_LINKING, "maven", null, null); fail("Expected exception was not thrown"); } catch (SolicitorRuntimeException e) { // we check detailed message to make sure the exception is not thrown due to other reasons diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java index d4a5fac4..1ea36e16 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensechecker/NpmLicenseCheckerReaderTests.java @@ -34,7 +34,7 @@ public NpmLicenseCheckerReaderTests() { gr.setModelFactory(modelFactory); gr.setInputStreamFactory(new FileInputStreamFactory()); gr.readInventory("npm-license-checker", "src/test/resources/npmLicenseCheckerReport.json", this.application, - UsagePattern.DYNAMIC_LINKING, "npm", null); + UsagePattern.DYNAMIC_LINKING, "npm", null, null); } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReaderTests.java index 086402d9..ee0dcbe4 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/npmlicensecrawler/NpmLicenseCrawlerReaderTests.java @@ -42,7 +42,7 @@ public void check(boolean warnOnly, String detailsString) { }); nr.setModelFactory(modelFactory); nr.setInputStreamFactory(new FileInputStreamFactory()); - nr.readInventory("npm", "src/test/resources/npmlicenses.csv", this.application, UsagePattern.DYNAMIC_LINKING, "npm", + nr.readInventory("npm", "src/test/resources/npmlicenses.csv", this.application, UsagePattern.DYNAMIC_LINKING, "npm", null, null); } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/ort/OrtReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/ort/OrtReaderTests.java index 29179aab..c97dca1d 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/ort/OrtReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/ort/OrtReaderTests.java @@ -33,7 +33,7 @@ public OrtReaderTests() { OrtReader pr = new OrtReader(); pr.setModelFactory(modelFactory); pr.setInputStreamFactory(new FileInputStreamFactory()); - pr.readInventory("ort", "src/test/resources/analyzer-result.json", this.application, UsagePattern.DYNAMIC_LINKING, "ort", + pr.readInventory("ort", "src/test/resources/analyzer-result.json", this.application, UsagePattern.DYNAMIC_LINKING, "ort", null, null); } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/piplicenses/PipReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/piplicenses/PipReaderTests.java index 8e0afe46..19e5689e 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/piplicenses/PipReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/piplicenses/PipReaderTests.java @@ -33,7 +33,7 @@ public PipReaderTests() { PipLicensesReader pr = new PipLicensesReader(); pr.setModelFactory(modelFactory); pr.setInputStreamFactory(new FileInputStreamFactory()); - pr.readInventory("pip", "src/test/resources/pipReport.json", this.application, UsagePattern.DYNAMIC_LINKING, "pip", + pr.readInventory("pip", "src/test/resources/pipReport.json", this.application, UsagePattern.DYNAMIC_LINKING, "pip", null, null); } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/yarn/YarnReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/yarn/YarnReaderTests.java index 83998d28..2ee47a1a 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/yarn/YarnReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/yarn/YarnReaderTests.java @@ -34,7 +34,7 @@ public YarnReaderTests() { yr.setModelFactory(modelFactory); yr.setInputStreamFactory(new FileInputStreamFactory()); yr.readInventory("yarn", "src/test/resources/yarnReport.json", this.application, UsagePattern.DYNAMIC_LINKING, - "yarn", null); + "yarn", null, null); } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReaderTests.java index dc02828e..49a96c00 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/yarnmodern/YarnModernReaderTests.java @@ -40,7 +40,7 @@ public YarnModernReaderTests() { yr.setModelFactory(modelFactory); yr.setInputStreamFactory(new FileInputStreamFactory()); yr.readInventory("yarn-modern", "src/test/resources/yarnModernReport.json", this.application, - UsagePattern.STATIC_LINKING, "npm", null); + UsagePattern.STATIC_LINKING, "npm", null, null); } diff --git a/core/src/test/resources/csvlicenses_npm.csv b/core/src/test/resources/csvlicenses_npm.csv new file mode 100644 index 00000000..86b82603 --- /dev/null +++ b/core/src/test/resources/csvlicenses_npm.csv @@ -0,0 +1,3 @@ +;@babel/code-frame;7.8.3;MIT;https://github.com/babel/babel/tree/master/packages/babel-code-frame +;@babel/compat-data;7.15.0;MIT;https://github.com/babel/babel.git +;accepts;1.3.7;MIT;https://github.com/jshttp/accepts.git \ No newline at end of file diff --git a/core/src/test/resources/csvlicenses_pypi.csv b/core/src/test/resources/csvlicenses_pypi.csv new file mode 100644 index 00000000..122853fc --- /dev/null +++ b/core/src/test/resources/csvlicenses_pypi.csv @@ -0,0 +1,3 @@ +;3.6.3;APScheduler;MIT;https://github.com/agronholm/apscheduler;; +;1.0.8;Keras-Applications;MIT;https://github.com/keras-team/keras-applications;; +;2.8.1;python-dateutil;Dual License;https://dateutil.readthedocs.io;; \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 01a0073d..60cdc923 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -149,6 +149,7 @@ The internal business data model consists of 6 entities: | artifactId | String | component identifier: maven artifactId | version | String | component identifier: Version | repoType | String | component identifier: RepoType +| packageType | String | component identifier: PackageType | packageUrl | String | the https://github.com/package-url/purl-spec[Package URL] as an technology neutral component identifier | noticeFileUrl | String | URL referencing a NOTICE file to be included in the attributions (optional, see <>) | noticeFileContent | String | resolved content of noticeFileUrl (optional, see <>) @@ -340,6 +341,7 @@ Within this section the different applications (=deliverables) of the engagement "source" : "classpath:samples/licenses_devon4j.xml", <7> <10> "usagePattern" : "DYNAMIC_LINKING", <8> "repoType" : "maven" <9> + "packageType" : "maven" <11> } ] } ], @@ -353,6 +355,7 @@ Within this section the different applications (=deliverables) of the engagement <8> usage pattern; possible values: `DYNAMIC_LINKING`, `STATIC_LINKING`, `STANDALONE_PRODUCT`; see description below <9> repoType: `repoType` to be set in the `ApplicationComponent` . *This parameter is deprecated and should no longer be used*, see <>. The value of `repoType` in `ApplicationComponent` will otherwise be determined from the type info in the PackageURL of the component. <10> _placeholder patterns might be used here_ +<11> packageType: type of the packages in the input data. Must be a valid packageUrl type (see https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst). Relevant when using the CSV reader. ===== Usage Patterns The usage pattern describes how the ``ApplicationComponent``s (libraries, packages) which are read in via the Reader are linked (in)to the ``Application``s executable. The kind of linking might affect the legal evaluation of the license compliance. @@ -550,7 +553,8 @@ In _Solicitor_ the data is read with the following part of the config "readers" : [ { "type" : "csv", "source" : "file:path/to/the/file.csv", - "usagePattern" : "DYNAMIC_LINKING" + "usagePattern" : "DYNAMIC_LINKING", + "packageType": "maven" } ] ---- @@ -621,7 +625,6 @@ These configurations may also be used to overwrite options of a https://commons. Important: In case that a component has multiple licenses attached, there needs to be a separate line in the csv file for each license. -WARNING: The CSV reader currently does not fill the attribute `packageUrl`. Any functionality/reporting based on this attribute will be disfunctional for data read by the CSV reader. === NPM @@ -1307,7 +1310,7 @@ The Generic Excel Writer exists purely for debugging purposes. This writer write "APPLICATIONCOMPONENT" : "classpath:com/devonfw/tools/solicitor/sql/allden_applicationcomponents.sql", "LICENSE" : "classpath:com/devonfw/tools/solicitor/sql/allden_normalizedlicenses.sql", "OSSLICENSES" : "classpath:com/devonfw/tools/solicitor/sql/ossapplicationcomponents.sql", - ... + ... } } ] @@ -1574,7 +1577,7 @@ artifacts: - name: pkg/npm/@somescope/somepackage/1.2.3 <1> url: https://github.com/foo/bar <2> licenseCurations: <3> - - operation: REMOVE + - operation: REMOVE path: "sources/package/readme.md" ruleIdentifier: "proprietary-license_unknown_13.RULE" matchedText: ".* to be paid .*" @@ -1603,8 +1606,8 @@ artifacts: <7> Further packages to follow. ===== Rules for curating licenses -Curating licenses is done by REMOVING (i.e. ignoring) specific license findings from ScanCode, by REPLACING the detected license with another one or by ADDING license findings either to specific files or on top level (not related to specific file of the package sources). -In addition to the conditions/data which is specific for any of the below described operations it is always possible to define a comment which is intended to be included in any audit trail log for documentation purposes (not yet used/implemented). +Curating licenses is done by REMOVING (i.e. ignoring) specific license findings from ScanCode, by REPLACING the detected license with another one or by ADDING license findings either to specific files or on top level (not related to specific file of the package sources). +In addition to the conditions/data which is specific for any of the below described operations it is always possible to define a comment which is intended to be included in any audit trail log for documentation purposes (not yet used/implemented). ====== Licenses: REMOVE @@ -1624,7 +1627,7 @@ Instead of removing licenses (ignoring the finding) they might be replaced with Data: * `newLicense` is the key / id of the license to use instead (replacing `files[].licenses[].spdx_license_key`) -* `url` is the url pointing to the license text +* `url` is the url pointing to the license text At least one of the two parameters has to be set. @@ -1658,14 +1661,14 @@ At least one of the conditions has to be defined. This follows the above principles. It uses the same conditions as REMOVE and uses a parameter to define the copyright to use instead: Data: -* `newCopyright`: The copyright entry to use instead of the originally found copyright +* `newCopyright`: The copyright entry to use instead of the originally found copyright ====== Copyright: ADD Adding new copyrights is done by defining rules which add new copyright info (to the copyrights found in a source file) - or "on top level". Conditions: -* `path` of the file within the sources (defined as a regular expression; if omitted the copyright will be applied on "top level"). +* `path` of the file within the sources (defined as a regular expression; if omitted the copyright will be applied on "top level"). Note that it is again only possible to add copyrigts to paths which are listed in the scancode json Data: @@ -1712,9 +1715,9 @@ artifacts: ===== Hierarchical definition of rules -Different version of a package/component or even different packages/components within the same namespace often require mostly the same curations to be applied. To avoid being forced to redefine curations for every single version it is possible to define curations by just specifying a prefix part in the name attribute. +Different version of a package/component or even different packages/components within the same namespace often require mostly the same curations to be applied. To avoid being forced to redefine curations for every single version it is possible to define curations by just specifying a prefix part in the name attribute. -Example of available levels/prefixes for `pkg:/maven/ch.qos.logback/logback-classic@1.2.3` +Example of available levels/prefixes for `pkg:/maven/ch.qos.logback/logback-classic@1.2.3` * `pkg` * `pkg/maven` @@ -1897,6 +1900,7 @@ Spring beans implementing this interface will be called at certain points in the == Release Notes 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'. Changes in 1.24.2:: * https://github.com/devonfw/solicitor/pull/271: Fixed an incompatibility with JDK 8. From 5ddce94c041cbb46a5ac01889f1c05db4cbf39f3 Mon Sep 17 00:00:00 2001 From: duph97 Date: Fri, 26 Jul 2024 12:48:11 +0200 Subject: [PATCH 138/139] Bugfix/cyclonedx reader license expression (#280) * 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> --- .../reader/cyclonedx/CyclonedxReader.java | 50 ++++++----- .../cyclonedx/CyclonedxReaderTests.java | 30 +++++++ core/src/test/resources/expressionsbom.json | 84 +++++++++++++++++++ documentation/master-solicitor.asciidoc | 1 + 4 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 core/src/test/resources/expressionsbom.json diff --git a/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java b/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java index e6258cd2..9d8c495e 100644 --- a/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java +++ b/core/src/main/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReader.java @@ -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); + } } } } diff --git a/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java b/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java index 7e68c505..1b2fda39 100644 --- a/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java +++ b/core/src/test/java/com/devonfw/tools/solicitor/reader/cyclonedx/CyclonedxReaderTests.java @@ -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); + } } diff --git a/core/src/test/resources/expressionsbom.json b/core/src/test/resources/expressionsbom.json new file mode 100644 index 00000000..3311faa4 --- /dev/null +++ b/core/src/test/resources/expressionsbom.json @@ -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": [] +} \ No newline at end of file diff --git a/documentation/master-solicitor.asciidoc b/documentation/master-solicitor.asciidoc index 60cdc923..cdf8bf02 100644 --- a/documentation/master-solicitor.asciidoc +++ b/documentation/master-solicitor.asciidoc @@ -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. From 5b73c050ad8ee735f46dcb03fa1da7a54330f092 Mon Sep 17 00:00:00 2001 From: Oliver Hecker <8004361+ohecker@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:19:26 +0200 Subject: [PATCH 139/139] Version 1.25.0 --- app/pom.xml | 2 +- core/pom.xml | 2 +- documentation/pom.xml | 2 +- logo/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 00df40b0..e1924684 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-app - 1.25.0-SNAPSHOT + 1.25.0 Solicitor App (Main Application) A tool for rule based checking of license conditions in software projects. diff --git a/core/pom.xml b/core/pom.xml index 0f725377..f14baf38 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-core - 1.25.0-SNAPSHOT + 1.25.0 Solicitor Core (Main Library) A tool for rule based checking of license conditions in software projects. diff --git a/documentation/pom.xml b/documentation/pom.xml index 04fb24a0..715836b5 100644 --- a/documentation/pom.xml +++ b/documentation/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-documentation - 1.25.0-SNAPSHOT + 1.25.0 pom Solicitor Documentation diff --git a/logo/pom.xml b/logo/pom.xml index d2c61edb..48af6137 100644 --- a/logo/pom.xml +++ b/logo/pom.xml @@ -13,7 +13,7 @@ com.devonfw.tools solicitor-logo - 1.25.0-SNAPSHOT + 1.25.0 Solicitor Logo Rendering Rendering of the Solicitor Logo diff --git a/pom.xml b/pom.xml index 57ec5b16..0d1266a4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.devonfw.tools solicitor-aggregator - 1.25.0-SNAPSHOT + 1.25.0 pom Solicitor Aggregator

        NameVersionPackage URL Source Code Repository Licenses Copyrights (or Authors/Contributors)$license.artifactId$license.version$license.sourceRepoUrl$license.artifactId$license.version$license.packageUrl + #if( "$!license.sourceRepoUrl" != "" ) + $license.sourceRepoUrl + #else + $!license.sourceDownloadUrl + #end + #foreach($ac in $NONCOMMERCIALCOMPONENTS_LICENSES ) - #if( $aid == $ac.artifactId && $gid == $ac.groupId && $ver == $ac.version ) + #if( $purl == $ac.packageUrl ) ## #set( $key = "${gid}${aid}${ac.normalizedLicense}" ) ## this is not necessarily sufficient as uinque id because version is missing ## $ac.normalizedLicense ## linking does not work currently $ac.normalizedLicense @@ -89,7 +96,11 @@ The following table lists all third party Open Source Components that may be con #end #end $esc.newLinesAsBreaks($esc.html($license.copyrights))$esc.newLinesAsBreaks($esc.html($license.copyrights))